package td2jira.jira.xmlrpc;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

import td2jira.jira.IJIRAConnector;
import td2jira.jira.JIRAComment;
import td2jira.jira.JIRAIssue;

public class JIRAXmlRpcConnector implements IJIRAConnector {
	private static Logger logger = Logger.getLogger(JIRAXmlRpcConnector.class);
	
	public static final String XML_RPC = "/rpc/xmlrpc";
    private XmlRpcClient rpcClient;

    private String token;
    private String projectName;
    
	public void login(String url,String projectName,String user,String password) {
		try {
	        XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
	        config.setServerURL(new URL(url+XML_RPC));
	
	        this.projectName = projectName;
	        
	        rpcClient = new XmlRpcClient();
	        rpcClient.setConfig(config);
	
	        token = (String) rpcClient.execute("jira1.login", params(user,password));
	        
	        Object[] ss = (Object[]) rpcClient.execute("jira1.getStatuses", params(token));
	        for (Object object : ss) {
	        	Map m = (Map) object;
				JIRAIssue.statuses.put((String)m.get("id"),(String)m.get("name"));
			}
	        
	        ss = (Object[]) rpcClient.execute("jira1.getResolutions", params(token));
	        for (Object object : ss) {
	        	Map m = (Map) object;
				JIRAIssue.resolutions.put((String)m.get("id"),(String)m.get("name"));
			}
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}
	
	public void logout() {
		try {
			rpcClient.execute("jira1.logout", params(token));
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	public List<JIRAIssue> findTasks(String query) {
		try {
			Object[] objects = (Object[]) rpcClient.execute("jira1.getIssuesFromTextSearch", params(token,query));
			Map<String,Object>[] maps = convertToMapArray(objects);
			
			List<JIRAIssue> tasks = new ArrayList<JIRAIssue>();
			for (Map<String,Object> map : maps) {
				JIRAIssue jt = JIRAIssue.fromMap(map);
				tasks.add(jt);
			}
			
			return tasks;
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	public List<JIRAComment> getComments(JIRAIssue jt) {
		List<JIRAComment> comments = new ArrayList<JIRAComment>();
		try {
			Object[] v = (Object[]) rpcClient.execute("jira1.getComments", params(token,jt.getKey()));
			
			for (Object object : v) {
				Map cm = (Map) object;
				
				JIRAComment jc = new JIRAComment();
				
				jc.setAuthor((String) cm.get("username"));
				if( jc.getAuthor() == null ) jc.setAuthor((String) cm.get("author"));
				
				jc.setBody((String) cm.get("body"));
				
				jc.setCreated((String) cm.get("timePerformed"));
				if( jc.getCreated() == null ) jc.setCreated((String) cm.get("created"));
				
				String created = jc.getCreated();
				if( created != null ) {
					int seconds = created.lastIndexOf(':');
					if( seconds >= 0 ) {
						created = created.substring(0, seconds);
						jc.setCreated(created);
					}
				}
				
				jc.setId((String) cm.get("id"));
				comments.add(jc);
				
				Map user = getUser(jc.getAuthor());
				jc.setAuthorFullName((String) user.get("fullname"));
			}
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
		return comments;
	}
	
	public Map[] getProjects() {
		try {
			Object[] objects = (Object[]) rpcClient.execute("jira1.getProjects",params(token));
			Map[] projects = convertToMapArray(objects);
			return projects;
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	private static Map<String,Map> users = new HashMap<String, Map>();
	public Map getUser(String userName) {
		try {
			if( users.get(userName) != null ) return users.get(userName);
			
			Map user = (Map) rpcClient.execute("jira1.getUser",params(token,userName));
			users.put(userName, user);
			return user;
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	 
	private static Map[] versions = null;
	public Map[] getVersions() {
		try {
			if( versions != null ) return versions;
			
			Object[] objects = (Object[]) rpcClient.execute("jira1.getVersions",params(token,projectName));
			versions = convertToMapArray(objects);
			return versions;
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	private static Map[] components = null;
	public Map[] getComponents() {
		try {
			if( components != null ) return components;
			
			Object[] objects = (Object[]) rpcClient.execute("jira1.getComponents",params(token,projectName));
			components = convertToMapArray(objects);
			return components;
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}
	
	public void createJIRATask(JIRAIssue jtd) {
		try {
	        HashMap<String,Object> issue = new HashMap<String,Object>();
	        issue.put("summary",jtd.getSummary());
	        issue.put("description",jtd.getDescription());
	        issue.put("project", jtd.getProjectId());
	        issue.put("type", 1);
	        issue.put("assignee", jtd.getAssignee());
	
	        Map[] versions = getVersions();
	        for (Map map : versions) {
	        	String name = (String) map.get("name"); 
	        	if( name.indexOf("Phase II - SIT") >=0 ) {
	        		Vector v = new Vector();
	        		v.add(map);
	        		issue.put("affectsVersions", v);
	        		issue.put("fixVersions",v); 
	        	}
			}
	
	        Map[] components = getComponents();
	        for (Map map : components) {
	        	String name = (String) map.get("name"); 
	        	if( name.indexOf("Presentation") >=0 ) {
	        		Vector v = new Vector();
	        		v.add(map);
	        		issue.put("components", v);
	        	}
			}
	        
	        Map created = (Map) rpcClient.execute("jira1.createIssue", params(token,issue));
	        jtd.setKey((String) created.get("key"));
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	private void updateSummary(JIRAIssue jiraIssue) {
		try {
			Map issue = (Map) rpcClient.execute("jira1.getIssue", params(token,jiraIssue.getKey()));
			issue.put("summary",jiraIssue.getSummary());
			Map issue2 = vectorizeValues(issue);
			Object o = rpcClient.execute("jira1.updateIssue",params(token,jiraIssue.getKey(),issue2));
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	@SuppressWarnings("unchecked")
	private Map<String,Object>[] convertToMapArray(Object[] objects) {
		Map[] ret = new Map[objects.length];
		for(int i=0; i<objects.length; ++i ){
			ret[i] = (Map<String,Object>)objects[i];
		}
		return ret;
	}

	public void addComment(JIRAIssue jt, String comment) {
		try {
			logger.info("adding JIRA comment to "+jt.getKey());
			logger.debug(comment);
	        rpcClient.execute("jira1.addComment", params(token,jt.getKey(),comment));
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	public void closeIssue(JIRAIssue jiraIssue) {
		try {
			final String closedMarker = "(CLOSED@TD)";
			String summary = jiraIssue.getSummary();
			if( summary.indexOf(closedMarker) >= 0 ) return;
			jiraIssue.setSummary(closedMarker+summary);
			logger.info("marking JIRA issue as closed: "+jiraIssue.getKey());
			updateSummary(jiraIssue);
		} catch( Exception ex ) {
			throw new RuntimeException(ex);
		}
	}

	private Map vectorizeValues(Map issue) {
		Map ret = new HashMap();
		Iterator<String> it = issue.keySet().iterator();
		while( it.hasNext() ) {
			String key = it.next();
			Object val = issue.get(key); 
			if( val instanceof Object[] ) {
				it.remove();
			} else {
				Vector v = new Vector();
				v.add(val);
				ret.put(key, v);
			}
		}
		return ret;
	}

	private Vector params(Object ... params) {
		Vector<Object> v = new Vector<Object>(params.length);
		for (Object object : params) {
			v.add(object);
		}
		return v;
	}

	public void addAttachment(JIRAIssue jiraIssue, String fileName, byte[] data) {
		logger.error(jiraIssue.getKey()+": adding attachment data for file "+fileName+" ("+data.length+" bytes) (NOT IMPLEMENTED IN XML-RPC)");
	}

	public List<String> getAttachmentsNames(JIRAIssue jiraIssue) {
		return new ArrayList<String>();
	}
}


