/*
 * This file is part of NixNote/NeighborNote 
 * Copyright 2011 Randy Baumgarte
 * 
 * This file may be licensed under the terms of of the
 * GNU General Public License Version 2 (the ``GPL'').
 *
 * Software distributed under the License is distributed
 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
 * express or implied. See the GPL for the specific language
 * governing rights and limitations.
 *
 * You should have received a copy of the GPL along with this
 * program. If not, go to http://www.gnu.org/licenses/gpl.html
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
*/

package cx.fbn.nevernote.xml;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import com.evernote.edam.type.Data;
import com.evernote.edam.type.Note;
import com.evernote.edam.type.NoteAttributes;
import com.evernote.edam.type.Resource;
import com.evernote.edam.type.ResourceAttributes;
import com.evernote.edam.type.Tag;
import com.trolltech.qt.core.QByteArray;
import com.trolltech.qt.core.QFile;
import com.trolltech.qt.core.QIODevice;
import com.trolltech.qt.core.QUuid;
import com.trolltech.qt.xml.QXmlStreamReader;

import cx.fbn.nevernote.sql.DatabaseConnection;
import cx.fbn.nevernote.utilities.ApplicationLogger;

public class ImportEnex {

	public int							lastError;
	private String						errorMessage;
	private String						fileName;
	DatabaseConnection					conn;
	QXmlStreamReader					reader;
	private Note						note;
	public int							highUpdateSequenceNumber;
	public long							lastSequenceDate;
	private final ApplicationLogger 	logger;
	private String						notebookGuid;
	public final boolean				importTags = false;
	public final boolean				importNotebooks = false;
	private String newGuid;
	List<Tag> tags;
	public boolean createNewTags;
	
	public ImportEnex(DatabaseConnection c, boolean full) {
		logger = new ApplicationLogger("import.log");
		conn = c;
		tags = conn.getTagTable().getAll();
		createNewTags = true;
	}
	
	public void importData(String f) {
		fileName = f;
		errorMessage = "";
				
		lastError = 0;
		errorMessage = "";
		QFile xmlFile = new QFile(fileName);
		if (!xmlFile.open(QIODevice.OpenModeFlag.ReadOnly)) {
			lastError = 16;
			errorMessage = "Cannot open file.";
		}
			
		reader = new QXmlStreamReader(xmlFile);	
		while (!reader.atEnd()) {
			reader.readNext();
			if (reader.hasError()) {
				errorMessage = reader.errorString();
				logger.log(logger.LOW, "************************* ERROR READING FILE " +reader.errorString());
				lastError = 16;
				return;
			}
			if (reader.name().equalsIgnoreCase("note") && reader.isStartElement()) {
				processNoteNode();
				note.setUpdateSequenceNum(0);
				if (notebookGuid != null) 
					note.setNotebookGuid(notebookGuid);
				for (int i=0; i<note.getResourcesSize(); i++) {
					note.getResources().get(i).setUpdateSequenceNum(0);
				}
				note.setActive(true);
				if (note.getUpdated() == 0) {
					note.setUpdated(note.getCreated());
				}
				conn.getNoteTable().addNote(note, true);
			}
		}
		xmlFile.close();
	}
	
	
	private void processNoteNode() {
		note = new Note();
		newGuid = QUuid.createUuid().toString().replace("{", "").replace("}", "");
		note.setGuid(newGuid);
		note.setResources(new ArrayList<Resource>());
		
		boolean atEnd = false;
		while(!atEnd) {
			if (reader.name().equalsIgnoreCase("title")) 
				note.setTitle(textValue());
			if (reader.name().equalsIgnoreCase("Created")) 
				note.setCreated(datetimeValue());
			if (reader.name().equalsIgnoreCase("updated")) 
				note.setCreated(datetimeValue());
			if (reader.name().equalsIgnoreCase("Content")) 
				note.setContent(textValue());
			if (reader.name().equalsIgnoreCase("tag") && createNewTags) {
				String tag = textValue();
				Tag noteTag = null;
				boolean found=false;
				for (int i=0; i<tags.size(); i++) {
					if (tags.get(i).getName().equalsIgnoreCase(tag)) {
						found=true;
						noteTag = tags.get(i);
						i=tags.size();
					}
				}
				
				if (!found) {
					noteTag = new Tag();
					noteTag.setName(tag);
					String tagGuid = QUuid.createUuid().toString().replace("{", "").replace("}", "");
					noteTag.setGuid(tagGuid);
					noteTag.setName(tag);
					tags.add(noteTag);
					conn.getTagTable().addTag(noteTag, true);
				}
				note.addToTagNames(noteTag.getName());
				note.addToTagGuids(noteTag.getGuid());
			}
			if (reader.name().equalsIgnoreCase("note-attributes")) 
				note.setAttributes(processNoteAttributes());
			if (reader.name().equalsIgnoreCase("resource")) {
				note.getResources().add(processResource());
			}
			reader.readNext();
			if (reader.name().equalsIgnoreCase("note") && reader.isEndElement())
				atEnd = true;
		}
		return;
	}	
	private Resource processResource() {
		Resource resource = new Resource();
		boolean atEnd = false;
		while(!atEnd) {
			if (reader.isStartElement() && reader.name().equalsIgnoreCase("resource")) {
				String newResGuid = QUuid.createUuid().toString().replace("{", "").replace("}", "");
				resource.setGuid(newResGuid);
				resource.setNoteGuid(this.newGuid);
			}
			if (reader.name().equalsIgnoreCase("mime")) 
				resource.setMime(textValue());
			if (reader.name().equalsIgnoreCase("height")) 
				resource.setHeight(shortValue());
			if (reader.name().equalsIgnoreCase("width")) 
				resource.setWidth(shortValue());
			if (reader.name().equalsIgnoreCase("data")) 
				resource.setData(processData("data"));
			if (reader.name().equalsIgnoreCase("resource-attributes")) 
				resource.setAttributes(processResourceAttributes());
			if (reader.name().equalsIgnoreCase("recognition")) 
				resource.setRecognition(processRecognition());
			reader.readNext();
			if (reader.name().equalsIgnoreCase("resource") && reader.isEndElement())
				atEnd = true;
		}
		if (resource.getAttributes() == null) 
			resource.setAttributes(new ResourceAttributes());
		conn.getNoteTable().noteResourceTable.updateNoteResource(resource, true);
		return resource;
	}
	
	private Data processData(String nodeName) {
		Data data = new Data();
		boolean atEnd = false;
		while(!atEnd) {
			if (reader.isStartElement()) {
				try {
				byte[] b = textValue().getBytes();   // data binary
				if (b.length > 0) {
					QByteArray hexData = new QByteArray(b);
					String hexString = hexData.toString();
					data.setBody(DatatypeConverter.parseBase64Binary(hexString));
					MessageDigest md;
					try {
						md = MessageDigest.getInstance("MD5");
						md.update(data.getBody());
						data.setBodyHash(md.digest());
					} catch (NoSuchAlgorithmException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
				}}
				catch (Exception e) {};
			}
			if (reader.name().equalsIgnoreCase(nodeName) && reader.isEndElement())
				atEnd = true;
			else 
				reader.readNext();
		}
		return data;
	}

	
	private NoteAttributes processNoteAttributes() {
		NoteAttributes attributes = new NoteAttributes();
	
		boolean atEnd = false;
		while(!atEnd) {
			if (reader.isStartElement()) {
				if (reader.name().equalsIgnoreCase("source-url")) 
					attributes.setSourceURL(textValue());
				if (reader.name().equalsIgnoreCase("source")) 
					attributes.setSource(textValue());
				if (reader.name().equalsIgnoreCase("longitude")) 
					attributes.setLongitude(doubleValue());
				if (reader.name().equalsIgnoreCase("latitude")) 
					attributes.setLatitude(doubleValue());
				if (reader.name().equalsIgnoreCase("altitude")) 
					attributes.setAltitude(doubleValue());
				if (reader.name().equalsIgnoreCase("author")) 
					attributes.setAuthor(textValue());
				if (reader.name().equalsIgnoreCase("subject-date")) 
					attributes.setSubjectDate(datetimeValue());
			}
			reader.readNext();
			if (reader.name().equalsIgnoreCase("note-attributes") && reader.isEndElement())
			atEnd = true;
		}
	
		return attributes;
	}

		
	
	private Data processRecognition() {
		Data reco = new Data();
		reco.setBody(textValue().getBytes());
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
			md.update(reco.getBody());
			reco.setBodyHash(md.digest());
			reco.setSize(reco.getBody().length);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return reco;
	}
	
	private ResourceAttributes processResourceAttributes() {
		ResourceAttributes attributes = new ResourceAttributes();
		boolean atEnd = false;
		while(!atEnd) {
			if (reader.isStartElement()) {
				if (reader.name().equalsIgnoreCase("camera-model")) 
					attributes.setCameraModel(textValue());		
				if (reader.name().equalsIgnoreCase("file-name")) 
					attributes.setFileName(textValue());		
				if (reader.name().equalsIgnoreCase("reco-type")) 
					attributes.setRecoType(textValue());		
				if (reader.name().equalsIgnoreCase("camera-make")) 
					attributes.setCameraMake(textValue());		
				if (reader.name().equalsIgnoreCase("source-url")) 
					attributes.setSourceURL(textValue());		
				if (reader.name().equalsIgnoreCase("Altitude")) 
					attributes.setAltitude(doubleValue());		
				if (reader.name().equalsIgnoreCase("Longitude")) 
					attributes.setLongitude(doubleValue());		
				if (reader.name().equalsIgnoreCase("Latitude")) 
					attributes.setLatitude(doubleValue());		
				if (reader.name().equalsIgnoreCase("Timestamp")) 
					attributes.setTimestamp(longValue());		
				if (reader.name().equalsIgnoreCase("Attachment")) 
					attributes.setAttachment(booleanValue());		
				if (reader.name().equalsIgnoreCase("ClientWillIndex")) 
					attributes.setClientWillIndex(booleanValue());		
			}
			reader.readNext();
			if (reader.name().equalsIgnoreCase("resource-attributes") && reader.isEndElement())
				atEnd = true;
		}
		
		return attributes;
	}
	
	
	private String textValue() {
		return reader.readElementText();
	}
	private long longValue() {
		return new Long(textValue());
	}
	private long datetimeValue() {
		Date d;
		String time = textValue();
		String year = time.substring(0,4);
		String month = time.substring(4,6);
		String day = time.substring(6,8);
		String hour = time.substring(9,11);
		String minute = time.substring(11,13);
		String second = time.substring(13,15);
		DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			d = dfm.parse(year +"-" +month +"-" +day +" " +hour +":" +minute +":" +second);
			return d.getTime();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}

	private boolean booleanValue() {
		String value = textValue();
		if (value.equalsIgnoreCase("true"))
			return true;
		else
			return false;
	}
	private short shortValue() {
		return new Short(textValue());
	}
	
	public void setNotebookGuid(String g) {
		notebookGuid = g;
	}
	
	public String getErrorMessage() {
		return errorMessage;
	}

	private double doubleValue() {
		return new Double(textValue());
	}
}
