package com.libreecm.commonAPI.model.objects;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.libreecm.commonAPI.auxiliars.exceptions.NotParseableUriFormatException;
import com.libreecm.commonAPI.model.enums.BaseTypeId;

/**
 * <p>Class that defines a path to a resource. It has one of the structures:
 * repositoryType://repositoryName/baseFolder/secondFolder/.../parentFolder/repositoryRoot
 * objectTypeId://repositoryName/objectBaseFolder/secondFolder/../parentFolder/object
 * For root folder, a example could be FolderId://RepositoryId/ where:</p>
 * 		<p>- Object is "" </p>
 * 
 * 		<p>- Folder tree is ""</p>
 * 
 * 	<p>Any object folder should finish with a slash</p>
 * 
 * 	<p>An Uri for a repository has its last element as a folder (the root folder) or table
 * An Uri for an object has its last element as a object</p>
 * @author Jordi Pérez Bayerri
 *
 */
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Uri {

	private String type;
	private String repositoryName;
	@JsonDeserialize(using = com.libreecm.commonAPI.auxiliars.JsonStringListDeserializer.class)
	private List<String> folderTree;
	private String object;
	
	
	
	/**
	 * Returns a string of the Uri of this object that is differs from Origen
	 * Example: "/object" if are at the same folder
	 * Example: "/baseFolder/..../parentFolder/object" if origen is at root folder
	 * Example: "../object" if origenObject is at a child folder
	 * @param origen
	 * @return
	 */
	/*public String getRelativePath (Uri origen){
		if (!origen.getRepositoryName().equals(this.getRepositoryName()))
			throw new NotEqualRepositoryIdException(origen.getRepositoryName(), this.getRepositoryName());
	
		//Case where origen folder is a parent of this element folder
		String result="";
		int i=0;
		for (int j=0;j<Math.min(origen.getFolderTreeList().size(), this.getFolderTreeList().size());j++){
			if (!origen.getFolderTreeList().get(i).equals(this.getFolderTreeList().get(i))){
				break;
			}
			i++;
		}
		System.out.println("Relative path in common: "+Arrays.asList(this.getFolderTreeList().toArray()).subList(0, i));
		//Adding as ../ as child folders we need
		int j=origen.getFolderTreeList().size();
		while (j>i){
			result=result.concat(".."+File.separatorChar);
			System.out.println("Now result is : "+result);
			j--;
		}
		while (i<this.folderTree.size()){ 
			result=result+this.folderTree.get(i)+File.separatorChar;
			System.out.println("And Now result is : "+result);
			i++;
		}
		System.out.println("Final result is "+result.concat(this.getObject()));
		return result.concat(this.getObject());
	}*/
	
	/**
	 * @return the type
	 */
	public String getType() {
		return type;
	}

	/**
	 * @param type the type to set
	 */
	public void setType(String type) {
		this.type = type;
	}

	/**
	 * @return the Id
	 */
	public String getRepositoryName()
	 {
		return repositoryName;
	}

	/**
	 * @param id the id to set
	 */
	public void setRepositoryName(String id) {
		this.repositoryName = id;
	}

	/**
	 * @return the folderTree as a List of Strings
	 */
	public List<String> getFolderTreeList() {
		if (folderTree==null)
			folderTree=new ArrayList<>();
		return folderTree;
	}

	/**
	 * @return folderTree the folderTree as a string
	 * Example: "/" or "/parentFolder" or "/baseFolder/parentFolder"
	 */
	public String getFolderTree(){
		String result=""+(File.separatorChar);
		for (String folder: this.getFolderTreeList()){
			if (!folder.equals(""))
				result=result.concat(folder+File.separatorChar);
		}
		return result;
	}
	
	/**
	 * @param folderTree the folderTree to set
	 */
	public void setFolderTree(List<String> folderTree) {
		this.folderTree = folderTree;
	}

	/**
	 * @param folderTree the folder tree as a String
	 */
	public void setFolderTree(String folderTree) {
		List<String> result = new ArrayList<String>();
		for (String folder : folderTree.split(File.pathSeparator)){
			result.add(folder);
		}
		this.setFolderTree(result);
	}

	/**
	 * @return the object
	 */
	public String getObject() {
		if (object==null)
			return "";
		return object;
	}

	/**
	 * @param object the object to set
	 */
	public void setObject(String object) {
		this.object = object;
	}

	/**
	 * @param path 
	 * @return Uri created with a valid string
	 */
	public static Uri parse(String path) {
		Uri result = new Uri();
		path=path.trim();
		//if (path.contains(" ")) throw new NotParseableUriFormatException(path+" couldn't have blank spaces.");
		if (path.contains("\\"))throw new NotParseableUriFormatException(path+" separator shoud be / .");
		if (path.contains("*")|| path.contains("?")||
				path.contains("\"")|| path.contains("<")||
				path.contains(">")|| path.contains("|")||
				path.contains("%")|| path.contains(",")||
				path.contains("#")|| path.contains("$")||
				path.contains("€")|| path.contains("!")||
				path.contains("+")|| path.contains("{")||
				path.contains("}")|| path.contains("&")||
				path.contains("[")|| path.contains("]")||
				path.contains("•")|| path.contains("'"))throw new NotParseableUriFormatException(path+" as special not valid characters.");
		String[] splitted = path.trim().split(String.valueOf(File.separatorChar));
		//Splitted should be: 
		//Splitted[0]= "repositoryType:" or "objectType:"
		//Splitted[1]= BLANK FOR THE DOUBLE SEPARATOR AT ://
		//Splitted[2]= "repositoryId"
		//Splitted[3]= "baseFolder"
		//Splitted[...
		//Splitted[x-1]= "parentFolder"
		//Splitted[x]= "object" or "" if a repository or folder
		
		//If should have at least 3 elements, repositoryType or objectType, BLANK or NULL, repositoryId, object or BLANK (if root folder)
		if (splitted.length<3) throw new NotParseableUriFormatException(path);
		int i=0;
		if (splitted[i].endsWith(":"))
			result.setType(splitted[i].substring(0, splitted[i].length()-1));
		else throw new NotParseableUriFormatException(path);
		i++;
		i++; //for the white space
		result.setRepositoryName(splitted[i]);
		i++;
		//Adding tree until the last element
		while (i<splitted.length-1){
			if (!splitted[i].equals(""))
					result.getFolderTreeList().add(splitted[i]);
			i++;
		}
		//If it's a repository set last element as tree
		//if it's a root folder, don't set anything in tree
		if (path.endsWith(String.valueOf(File.separatorChar))){
			if (splitted.length!=3)
				result.getFolderTreeList().add(splitted[i]);
			result.setObject("");	
		}else {	
			result.setObject(splitted[i]);
		}
		return result;
	}
	
	public String toString(){
		String result = type;
		result=result.concat("://");
		result=result.concat(repositoryName);
		result=result.concat(getFolderTree());
		if (!object.equals(""))
				result=result.concat(object);
		return result;
	}

	/**
	 * Createa a string that identifies the folder containing the object
	 * Default parent type is cmis:folder
	 * 
	 * For Uris like "cmis:document://First-repo/folder1/folder2/object.txt"
	 * return "cmis:folder://First-repo/folder1/folder2/"
	 * 
	 * 
	 * For Uris like "cmis:folder://First-repo/folder1/folder2/"
	 * return "cmis:folder://First-repo/folder1/"
	 * 
	 * 
	 * For Uris like "cmis:folder://First-repo/"
	 * return "cmis:folder://First-repo/"
	 * 
	 * @return
	 */
	public String getParentUri() {
			Uri parentUri= new Uri();
			int i=0;
			//Adding all folder tree except the last one
			while (i<this.getFolderTreeList().size()-1){
				parentUri.getFolderTreeList().add(this.getFolderTreeList().get(i));
				i++;
			}
			//if it's a root folder will throw error
			try{
				//If it's not a Folder
				if (this.getObject()!=null && !this.getObject().equals("")){
					parentUri.getFolderTreeList().add(this.getFolderTreeList().get(i));
				}
			} catch (Exception e){
			}
			parentUri.setObject("");
			parentUri.setRepositoryName(this.getRepositoryName());
			parentUri.setType(BaseTypeId.CMIS_FOLDER.value());	
			return parentUri.toString();
		}
	
}
