package com.akjava.gwt.lib.client.datalist;

import java.io.IOException;
import java.util.Date;
import java.util.List;

import com.akjava.gwt.html5.client.download.HTML5Download;
import com.akjava.gwt.html5.client.file.File;
import com.akjava.gwt.html5.client.file.FileUploadForm;
import com.akjava.gwt.html5.client.file.FileUtils;
import com.akjava.gwt.html5.client.file.FileUtils.DataURLListener;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.gwt.lib.client.StorageDataList;
import com.akjava.gwt.lib.client.datalist.functions.CsvArrayToHeadAndValueFunction;
import com.akjava.gwt.lib.client.datalist.functions.HeadAndValueToCsvFunction;
import com.akjava.gwt.lib.client.widget.TabInputableTextArea;
import com.akjava.lib.common.csv.CSVReader;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.TextArea;

/**
 * 
 * how to use
 * 
 * 
 * 
 * @author aki
 *
 */
public class TextAreaBasedDataList extends SimpleDataListItemControler{
private TextArea textArea;
private SimpleTextData copiedValue;
private HorizontalPanel uploadPanel;
private FileUploadForm uploadForm;

public static interface TextAreaBasedDataListListener{
	public void onLoad(Optional<SimpleTextData> hv);
}

/**
 * if you need call addkeyUpHandler() for update modified
 * @param textArea
 */
	public void setTextArea(TextArea textArea) {
	this.textArea = textArea;
}

	public TextArea getTextArea() {
	return textArea;
	}

	private void textModified(){
		getSimpleDataListWidget().setModified(true);//simple modified
	}
	
	public TextAreaBasedDataList(StorageDataList dataList) {
		this(dataList,null);
	}
	public TextAreaBasedDataList(StorageDataList dataList,SimpleTextData defaultOnEmptyData) {
		super(dataList);
		this.defaultOnEmptyData=defaultOnEmptyData;
		
		textArea=new TabInputableTextArea();
		
		
		addKeyHandler(textArea);
		 
		 
		 getSimpleDataListWidget().setCellContextMenu(new TestContextMenu());
		 
		 unselect();
		 
		 if(getDataList().getDataList().isEmpty()){
			
			 for(SimpleTextData data:getDefaultOnEmptyData().asSet()){
				
				 for(Integer id:add(data).asSet()){
					
					select(id); 
				 }
			 }
		 }
		 
	}
	private SimpleTextData defaultOnEmptyData;
	


	private Optional<SimpleTextData> getDefaultOnEmptyData(){
		return Optional.fromNullable(defaultOnEmptyData);
	}
	
	private boolean saved;
	public void addKeyHandler(TextArea text){
		text.addKeyDownHandler(new KeyDownHandler() {
			@Override
			public void onKeyDown(KeyDownEvent event) {
				if(event.isControlKeyDown()){
					if(event.getNativeKeyCode()==83){//ignore save
						event.preventDefault();
						saved=true;
						return;
					}
				}
				saved=false;
			}
		})
		;
		 text.addKeyUpHandler(new KeyUpHandler() {
				@Override
				public void onKeyUp(KeyUpEvent event) {
					if(saved){//ignore save key
						return;
					}
					//ignore single ctrl,this happen after save
					if(event.getNativeKeyCode()==KeyCodes.KEY_CTRL){
						return;
					}
					
					//LogUtils.log("key-up:"+event.getNativeKeyCode());
					if(event.getNativeKeyCode()==KeyCodes.KEY_ENTER){
						textModified();
					}
					else if(event.isControlKeyDown()){
						
						
						if(event.getNativeKeyCode()==83){//S(save)
							event.preventDefault();
							return;
						}
						
						if(event.getNativeKeyCode()!=65 && event.getNativeKeyCode()!=67){//ignore A(select all) && C(copy)
							textModified();
						}
						
					}
					else{
						textModified();
					}
				}
			});
	}

	/**
	 * This context menu is very primitive,so can't modified after create.used globally
	 * TODO 
	 * @author aki
	 *
	 */
	public class TestContextMenu extends AbstractContextMenu{
		@Override
		public void createMenus(MenuBar menuBar) {
			Command test1Command = new Command() {
				  public void execute() {
					SimpleTextData data=getSimpleDataListWidget().getSelection().getData();
					String currentDate=getSimpleDataListWidget().dateFormat.format(new Date(data.getCdate()));
					hide(); 
					String value=Window.prompt("New Date", currentDate);
				    if(value!=null){
				    	Date d=getSimpleDataListWidget().dateFormat.parse(value);
				    	data.setCdate(d.getTime());
				    	getSimpleDataListWidget().save();//it's work?
				    	getSimpleDataListWidget().redraw();
				    }
				  }
			};
			menuBar.addItem("Modify create date", test1Command);
			
		}
	}
	
	@Override
	public SimpleTextData createSaveData(String fileName) {
		String text=textArea.getText();
		SimpleTextData current=getSimpleDataListWidget().getSelection().getData();
		if(current==null){
		return new SimpleTextData(-1,fileName,text);
		}else{
			return new SimpleTextData(-1,fileName,text,current.getCdate());	//copy cdate
		}
	}

	@Override
	public SimpleTextData createNewData(String fileName) {
		String text="";
		return new SimpleTextData(-1,fileName,text);
	}

	protected String unselectedText="CREATE NEW or SELECT";
	
	private TextAreaBasedDataListListener textAreaBasedDataListListener;
	public TextAreaBasedDataListListener getTextAreaBasedDataListListener() {
		return textAreaBasedDataListListener;
	}

	public void setTextAreaBasedDataListListener(
			TextAreaBasedDataListListener textAreaBasedDataListListener) {
		this.textAreaBasedDataListListener = textAreaBasedDataListListener;
	}

	@Override
	public void loadData(Optional<SimpleTextData> hv) {
		if(hv.isPresent()){
			textArea.setReadOnly(false);
			textArea.setText(hv.get().getData());		
		}else{//unselected and clear
			textArea.setReadOnly(true);
			textArea.setText(unselectedText);
		}
		
		if(textAreaBasedDataListListener!=null){
			textAreaBasedDataListListener.onLoad(hv);
		}
		
		onLoad();
	}
	
	public void onLoad(){}
	
	/**
	 * used for export file name
	 * @return
	 */
	protected String getKeyName(){
		return "datas";
	}

	@Override
	public void exportDatas(List<SimpleTextData> list) {
		String exportText=generateExportText(list);
		Anchor anchor=new HTML5Download().generateTextDownloadLink(exportText,getKeyName()+".csv","Download data",true);
		getSimpleDataListWidget().add(anchor);
	}
	
	private String generateExportText(List<SimpleTextData> list){
		List<String> lines=FluentIterable.from(list).transform(new HeadAndValueToCsvFunction()).toList();
		String exportText=Joiner.on("\r\n").join(lines);
		return exportText;
	}
	
	public String generateExportText(){
		List<SimpleTextData> list=this.getDataList().getDataList();
		return generateExportText(list);
	}

	@Override
	public void importData() {
		if(getDataList().getDataList().size()>0){
		boolean confirm=Window.confirm("Import datas:add current datas with ignoring id,if you keep id use restore and replace all data.");
		if(!confirm){
			return;
		}
		}
		
		uploadForm = FileUtils.createSingleTextFileUploadForm(new DataURLListener() {
			@Override
			public void uploaded(File file, String value) {
				List<SimpleTextData> list=textToSimpleTextData(value);
				for(SimpleTextData hv:list){
					getDataList().addData(hv.getName(), hv.getData());
				}
				
				uploadPanel.removeFromParent();
				updateList();
			}
		}, false);
		
		
		createFormPanel("Import Datas:",uploadForm);
		getSimpleDataListWidget().add(uploadPanel);
	}

	@Override
	public void clearAll() {
		boolean confirm=Window.confirm("Clear all data?this operation can not undo\nyou should ExportAll first.");
		if(!confirm){
			return;
		}
		List<SimpleTextData> hvs= getDataList().getDataList();
		for(SimpleTextData hv:hvs){
			getDataList().clearData(hv.getId());
		}
		getDataList().setCurrentId(0);//restart id
		
		getSimpleDataListWidget().unselect();
		updateList();
		
	}
	

	public Optional<Integer> add(SimpleTextData data) {
		if(data!=null){
		int id=getDataList().addData(data.getName(), data.getCdate()+","+data.getData());
		updateList();
		return Optional.of(id);
		}
		return Optional.absent();
	}

	@Override
	public void copy(Object object) {
		copiedValue=(SimpleTextData) object;
	}

	@Override
	public void paste() {
		if(copiedValue!=null){
			SimpleTextData copy=copiedValue.copy();
			copy.setName(copy.getName()+" copy");
			execAdd(copy,true);
		//modified
		//getDataList().addData(copiedValue.getName()+" copy", copiedValue.getData());
		//updateList();
		}
	}

	@Override
	public void recoverLastSaved(SimpleTextData hv) {
		copy(hv);
		paste();
	}

	/**
	 * restore keep id
	 */
	@Override
	public void restore() {
		if(getDataList().getDataList().size()>0){
			boolean confirm=Window.confirm("Restore datas:clear current datas and restore csv datas.");
			if(!confirm){
				return;
			}
			}
		
		uploadForm = FileUtils.createSingleTextFileUploadForm(new DataURLListener() {
			@Override
			public void uploaded(File file, String value) {
				execRestore(value);
			}
		}, false);
		
		
		createFormPanel("Restore layers:",uploadForm);
		getSimpleDataListWidget().add(uploadPanel);
		
	}
	public void execRestore(String value){
		//clear first
		List<SimpleTextData> hvs= getDataList().getDataList();
		for(SimpleTextData hv:hvs){
			getDataList().clearData(hv.getId());
		}
		//getDataList().setCurrentId(0);
		
		int max=0;
		
		List<SimpleTextData> list=textToSimpleTextData(value);
		
		//do offset
		//LogUtils.log("force offset");
		//doOffset(parts.getKey(),list);
		
		GWT.log("upload-size:"+list.size());
		for(SimpleTextData hv:list){
			if(hv.getId()>max){
				max=hv.getId();
			}
			getDataList().updateData(hv.getId(),hv.getName(), hv.getData());
		}
		
		getDataList().setCurrentId(max+1);
		
		if(uploadPanel!=null){
			uploadPanel.removeFromParent();
		}
		updateList();
	}
	
	public void execRestore(List<SimpleTextData> list){
		//clear first
		List<SimpleTextData> hvs= getDataList().getDataList();
		for(SimpleTextData hv:hvs){
			getDataList().clearData(hv.getId());
		}
		//getDataList().setCurrentId(0);
		
		int maxId=0;
		
		
		//do offset
		//LogUtils.log("force offset");
		//doOffset(parts.getKey(),list);
		
		//LogUtils.log("upload-size:"+list.size());
		for(SimpleTextData hv:list){
			if(hv.getId()>maxId){
				maxId=hv.getId();
			}
			//LogUtils.log("update:"+hv.getId());
			getDataList().updateData(hv.getId(),hv.getName(), hv.getData());
		}
		
		getDataList().setCurrentId(maxId+1);
		
		//LogUtils.log("current-id:"+getDataList().getCurrentId());
		
		if(uploadPanel!=null){
			uploadPanel.removeFromParent();
		}
		selectedId=-1;
		updateList();
	}


	@Override
	public void doDoubleClick(int clientX, int clientY) {
		LogUtils.log("double-click:"+clientX+","+clientY);
	}
	
	private void createFormPanel(String text,FileUploadForm form){
		if(uploadPanel==null){
		uploadPanel=new HorizontalPanel();
		uploadPanel.add(new Label(text));
		uploadPanel.add(form);
		Button bt=new Button("close",new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				uploadPanel.setVisible(false);
			}
		});
		uploadPanel.add(bt);
		}else{
			uploadPanel.setVisible(true);
		}
	}

	//TODO move somewhere
	public static List<SimpleTextData> textToSimpleTextData(String text){
		//List<SimpleTextData> list=new ArrayList<SimpleTextData>();
		CSVReader reader=new CSVReader(text,'\t','"',true);
		try {
			List<String[]> csvs=reader.readAll();
			return FluentIterable.from(csvs).transform(new CsvArrayToHeadAndValueFunction()).toList();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Window.alert(e.getMessage());
		}
		return null;
	}
}
