package org.iri_research.renkan.models;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;
import org.iri_research.renkan.Constants;
import org.iri_research.renkan.Constants.EditMode;
import org.iri_research.renkan.RenkanException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import com.fasterxml.jackson.annotation.JsonProperty;

@Document(collection="projects")
public class Project extends AbstractRenkanModel<String> {
	
	@SuppressWarnings("unused")
	private static Logger logger = LoggerFactory.getLogger(Project.class); 
			
	@Field("rev_counter")
	private int revCounter = 1;
	
	private Date created;
	private Date updated;
	
	// Space
	@Field("space_id")
	@JsonProperty("space_id")
	private String spaceId = null;
	
	// Nodes
	@DBRef
	private List<Node> nodes = new ArrayList<Node>();
	
	// edges
	@DBRef
	private List<Edge> edges = new ArrayList<Edge>();

	// Users
	@DBRef
	private List<User> users = new ArrayList<User>();
	
	
	public Project(Project project) {
		this(project.spaceId, Constants.UUID_GENERATOR.generate().toString(), project.title, project.description, project.uri, new Date());
		
		Map<String, Node> nodeCloneMap = new HashMap<String, Node>(project.nodes.size());
		for (Node node : project.nodes) {
			Node newNode = new Node(node,this.id);
			this.nodes.add(newNode);
			nodeCloneMap.put(node.id, newNode);
		}

		for (Edge edge : project.edges) {
			this.edges.add(new Edge(edge, nodeCloneMap.get(edge.getFrom()), nodeCloneMap.get(edge.getTo()), this.id));
		}
		for(User user : project.users) {
			this.users.add(user);
		}
	}
	
	public Project(String spaceId, String id, String title, String description, String uri, Date created,
			int revCounter) {
		super(id,title, description, uri, null);
		this.revCounter = revCounter;
		this.spaceId = spaceId;
		this.created = created;
		if(this.created == null) {
			this.created = new Date();
		}
		this.setUpdated(new Date()); 
	}

	@Autowired(required=true)
	public Project(String spaceId, String id, String title, String description, String uri, Date created) {
		this(spaceId, id,title, description, uri, created, 1);
	}
	
	@SuppressWarnings("unused")
	private Project() {		
	}
		
	public int getRevCounter() {
		return this.revCounter;
	}
	
	public List<Node> getNodes() {
		return this.nodes;
	}

	public List<Edge> getEdges() {
		return this.edges;
	}
	
	public List<User> getUsers() {
		return this.users;
	}

	public Date getCreated() {
		return created;
	}
	
	public void setCreated(Date date) {
		this.created = date;
		
	}
	
	@JsonProperty("space_id")
	public String getSpaceId() {
		return spaceId;
	}

	private String getRawKey(String prefix, Constants.EditMode editMode) {		
		StringBuffer key = new StringBuffer(prefix!=null?prefix+"|":"");
		key.append(this.getId());
		key.append('|');
		key.append(this.getSpaceId());
		key.append('|');
		key.append(this.getCreated().getTime());
		key.append('|');
		key.append(editMode.toString());
		return key.toString();
	}
	
	public String getKey(int editMode) throws RenkanException {		
		return this.getKey(EditMode.fromInt(editMode));
	}
	
	public String getKey(Constants.EditMode editMode) throws RenkanException {
		
		String rawKey = this.getRawKey("", editMode);
		
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("SHA-256");
		} catch (NoSuchAlgorithmException e) {
			throw new RenkanException("NoSuchAlgorithmException digest: " + e.getMessage(), e);
		}
		String key;
		final SecretKeySpec secret_key = new SecretKeySpec(Constants.KEYHEX.getBytes(), "HmacSHA256");
		md.update(secret_key.getEncoded());
		try {
			key = Hex.encodeHexString(md.digest(rawKey.getBytes("UTF-8")));
		} catch (UnsupportedEncodingException e) {
			throw new RenkanException("UnsupportedEncodingException digest: " + e.getMessage(), e);
		}
		
		return key;
	}
	
	public boolean checkKey(String key, Constants.EditMode editMode) throws RenkanException {
		

		if(key == null || key.isEmpty()) {
			return false;
		}
				
		String signature = key;
		
		String new_key = this.getKey(editMode);
		
		return new_key.equals(signature);
	}

	public Date getUpdated() {
		return updated;
	}

	public void setUpdated(Date updated) {
		this.updated = updated;
	}
	
}
