package jp.co.corecorp.s3pdf;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

public class S3Resource {
	private S3Application service;
	private String rid = null;
	private String resourceName = null;
	private String title = null;
	private String description = null;
	private Date lastSyncTime = null;
	private Date lastModiTime = null;
	private Date expireTime = null;
	private Date removeTime = null;
	private long size = 0;
	private List<String>	tags = null;
	private List<S3Gadget>	gadgetArray = null;
	private List<TagMap> 	gadgetTagMap = null;
	private class TagMap {
		public String tag;
		public List<S3Gadget> gadget;
		public TagMap(String tag) {
			this.tag = tag;
			this.gadget = new ArrayList<S3Gadget>();
		}
		public void addGadget(S3Gadget g) {
			gadget.add(g);
		}
		public void removeGadget(S3Gadget g) {
			int n = gadget.size();
			for( int i = 0; i < n; i++ ) {
				if( gadget.get(i) == g ) {
					gadget.remove(i);
					break;
				}
			}
		}
		public int getCount() {
			return gadget.size();
		}
		public S3Gadget getGadget(int index) {
			return gadget.get(index);
		}
	}

	public S3Resource(S3Application svc, String rsn) {
		service	= svc;
		resourceName = rsn;
	}
	public String getResourceId() {
		return rid;
	}
	public String getResourceName() {
		return resourceName;
	}
	public String getTitle() {
		return title;
	}
	public String getDescription() {
		return description;
	}
	public Date getLastSyncTime() {
		return lastSyncTime;
	}
	public Date getExpireTime() {
		return expireTime;
	}

	public String getDemoFilePath() throws S3Exception {
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		return lc.getDataPath(service, this);
	}
	
	public void doSync(Handler handler) throws S3SyncException {
		Bundle startNotifyData = new Bundle();
		startNotifyData.putInt("C", S3Context.START_RESOURCE_SYNC);
		Message startNotifyMsg = Message.obtain();
		startNotifyMsg.setData(startNotifyData);
		handler.sendMessage(startNotifyMsg);

		S3Context s3ctx = service.getContext();
		if( s3ctx.isOnline() ) {
			
		}
		
		Bundle endNotifyData = new Bundle();
		endNotifyData.putInt("C", S3Context.END_RESOURCE_SYNC);
		Message endNotifyMsg = Message.obtain();
		endNotifyMsg.setData(endNotifyData);
		handler.sendMessage(endNotifyMsg);
	}
	
	public void loadInfo() throws S3Exception, S3TypeException {
		rid = null;
		title = null;
		description = null;
		lastSyncTime = null;
		lastModiTime = null;
		removeTime = null;
		expireTime = null;
		tags = null;
		gadgetArray = null;
		gadgetTagMap = null;
		
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		JSONObject inf = lc.loadResourceInfo(service, this);
		if( inf == null ) {
			return;
		}
		SimpleDateFormat dfm = new SimpleDateFormat(SharePDF.RESOURCE_TIMEFMT);
		String dtv = null;
		try {
			rid	= inf.getString(SharePDF.RESOURCE_ID);
			if( inf.has(SharePDF.RESOURCE_TITLE)) {
				title = inf.getString(SharePDF.RESOURCE_TITLE);
			}
			if( inf.has(SharePDF.RESOURCE_DESC)) {
				description = inf.getString(SharePDF.RESOURCE_DESC);
			}
			if( inf.has(SharePDF.RESOURCE_SYNCTIME) ) {
				dtv = inf.getString(SharePDF.RESOURCE_SYNCTIME);
				lastSyncTime = dfm.parse(dtv);
			}
			if( inf.has(SharePDF.RESOURCE_MODIFYTIME) ) {
				dtv = inf.getString(SharePDF.RESOURCE_MODIFYTIME);
				lastModiTime = dfm.parse(dtv);
			}
			if( inf.has(SharePDF.RESOURCE_REMOVETIME) ) {
				dtv = inf.getString(SharePDF.RESOURCE_REMOVETIME);
				removeTime = dfm.parse(dtv);
			}
			if( inf.has(SharePDF.RESOURCE_EXPIRETIME) ) {
				dtv = inf.getString(SharePDF.RESOURCE_EXPIRETIME);
				expireTime = dfm.parse(dtv);
			}
			if( inf.has(SharePDF.RESOURCE_TAGS)) {
				JSONArray tag = inf.getJSONArray(SharePDF.RESOURCE_TAGS);
				ArrayList<String> newtags = new ArrayList<String>();
				int n = tag.length();
				int i;
				for( i = 0; i < n; i++ ) {
					String v = tag.getString(i);
					newtags.add(v);
				}
				tags = newtags;
			}
		} catch (JSONException e) {
		} catch (ParseException e) {
		}
		size = lc.getSize(service, this);
	}
	
	public void saveInfo() throws S3Exception {
		/*
		{
			RESOURCE_ID:"R00000008",
			"title":"#10-392003462 小渕 優子",
			RESOURCE_TAGS:["$$SharePDF","card"],
			RESOURCE_DESC:"4月12日木曜日",
			RESOURCE_SYNCTIME:"2012/09/07 15:16:29"
		}
		*/
		SimpleDateFormat dfm = new SimpleDateFormat(SharePDF.RESOURCE_TIMEFMT);

		JSONObject js = new JSONObject();
		try {
			js.put(SharePDF.RESOURCE_ID, rid);
			if( title != null ) {
				js.put(SharePDF.RESOURCE_TITLE, title);
			}
			if( description != null ) {
				js.put(SharePDF.RESOURCE_DESC,description);
			}
			if( lastModiTime != null ) {
				js.put(SharePDF.RESOURCE_MODIFYTIME, dfm.format(lastModiTime));
			}
			if( lastSyncTime != null ) {
				js.put(SharePDF.RESOURCE_SYNCTIME, dfm.format(lastSyncTime));
			}
			if( expireTime != null ) {
				js.put(SharePDF.RESOURCE_EXPIRETIME, dfm.format(expireTime));
			}
			if( removeTime != null ) {
				js.put(SharePDF.RESOURCE_REMOVETIME, dfm.format(removeTime));
			}
			if( tags != null && !tags.isEmpty() ) {
				JSONArray arr = new JSONArray();
				int n = tags.size();
				for( int i = 0; i < n; i++ ) {
					arr.put(tags.get(i));
				}
				js.put(SharePDF.RESOURCE_TAGS, arr);
			}
		} catch (JSONException e) {
			e.printStackTrace();
			throw new S3Exception(e.toString());
		}
		
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		lc.saveResourceInfo(service, this, js);
	}
	
	public InputStream openInputStream() throws S3Exception {
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		return lc.openData(service, this);
	}
	public long getSize() {
		return size;
	}
	
	//// tag
	public boolean hasTag(String tag) {
		int n = tags.size();
		int i;
		for( i = 0; i < n; i++ ) {
			if( tags.get(i).equals(tag) ) return true;
		}
		return false;
	}
	public void setTag(String tag) throws S3Exception {
		if( !hasTag(tag) ) { 
			tags.add(tag);
			saveInfo();
		}
	}
	public void removeTag(String tag) throws S3Exception {
		int n = tags.size();
		int i;
		for( i = 0; i < n; i++ ) {
			if( tags.get(i).equals(tag) ) {
				tags.remove(i);
				saveInfo();
				return;
			}
		}
	}
	
	public List<String> getTags() {
		return tags;
	}
	
	//// gadgets
	public void loadGadget() throws S3Exception, S3TypeException {
		gadgetArray		= null;
		gadgetTagMap	= null;
		
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();

		InputStream is = lc.openGadget(service, this);
		if( is == null ) return;
		
		StringBuffer buff = null;
		try {
			InputStreamReader in = new InputStreamReader(is, "UTF-8");
			char[] chunk= new char[512];
			buff = new StringBuffer(512);
			while(true) {
				int rs = in.read(chunk);
				if( rs < 0 ) break;
				buff.append(chunk);
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			JSONObject js = new JSONObject(buff.toString());
			JSONArray gadArray = js.getJSONArray("gadget");
			int n = gadArray.length();
			int i;
			for( i = 0; i < n; i++ ) {
				JSONObject gadObj = gadArray.getJSONObject(i);
				S3Gadget g = null;
				try {
					g = S3Gadget.createFromJSON(gadObj);
				}
				catch(S3TypeException e) {
					continue;
				}
				if( gadgetArray == null )
					gadgetArray = new ArrayList<S3Gadget>();
				gadgetArray.add(g);
			}
		} catch (JSONException e) {
		}
	}
	
	public void saveGadget() throws S3Exception, S3TypeException {
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		if( gadgetArray != null && !gadgetArray.isEmpty() ) {
			int n = gadgetArray.size();
			if( n == 0 ) return;
			JSONObject js = new JSONObject();
			JSONArray arr = new JSONArray();
			int i;
			for( i = 0; i < n; i++ ) {
				S3Gadget g = gadgetArray.get(i);
				if( g.getType() != SharePDF.GADGET_EMPTY ) {
					JSONObject gad = g.saveToJSON();
					arr.put(gad);
				}
			}
			try {
				js.put("gadget", arr);
			} catch (JSONException e) {
				throw new S3TypeException("gadget type error");
			}
			String s = js.toString();
			InputStream is = new ByteArrayInputStream(s.getBytes()); 
			lc.saveGadget(service, this, is);
		} else {
			lc.removeGadget(service, this);
		}
	}

	public synchronized String createUniqueId() {
		String id;
		double ri;
		while( true ) {
			ri = Math.random();
			id = String.format("%8x", (long)(0x7fffffff*ri));
			if( findGadget(id) == null ) {
				S3Gadget temp = new S3Gadget(id);
				if( gadgetArray == null )
					gadgetArray = new ArrayList<S3Gadget>();
				gadgetArray.add(temp);
				return id;
			}
		}
	}
	
	public int getGadgetCount() {
		if( gadgetArray == null ) {
			return 0;
		}
		return gadgetArray.size();
	}
	public String getGadgetId(int pos) {
		if( gadgetArray == null ) {
			return null;
		}
		if( pos < 0 || gadgetArray.size() <= pos ) {
			return null;
		}
		return gadgetArray.get(pos).getId();
	}

	public S3Gadget findGadget(String id) {
		if( gadgetArray == null )
			return null;
		int n = gadgetArray.size();
		int i;
		for( i = 0; i < n; i++ ) {
			S3Gadget g = gadgetArray.get(i);
			if( g.getId().equals(id) ) {
				if( g.getLastRemoveTime() != null )
					return null;
				return g;
			}
		}
		return null;
	}
	
	private TagMap findGadgetTagMap(List<S3Resource.TagMap> map, String tag) {
		if( map == null )
			return null;
		int n = map.size();
		int i;
		for( i = 0; i < n; i++ ) {
			TagMap tm = map.get(i); 
			if( tm.tag.equals(tag) ) return tm; 
		}
		return null;
	}
	private void createGadgetTagMap(List<S3Resource.TagMap> map, S3Gadget g, String tag) {
		TagMap tm = findGadgetTagMap(map, tag);
		if( tm == null ) {
			tm = new TagMap(tag);
			map.add(tm);
		}
		tm.addGadget(g);
	}
	private void addGadgetTagMap(S3Gadget g, String tag) {
		if( gadgetTagMap == null )
			gadgetTagMap = new ArrayList<S3Resource.TagMap>();
		createGadgetTagMap(gadgetTagMap, g, tag);
		if( gadgetTagMap.isEmpty() )
			gadgetTagMap = null;
	}
	private void createGadgetTagMap(List<S3Resource.TagMap> map, S3Gadget g) {
		ArrayList<String> tags = g.getTags();
		if( tags != null ) {
			int m = tags.size();
			int t;
			for( t = 0; t < m; t++ ) {
				String tag = tags.get(t);
				createGadgetTagMap(map, g, tag);
			}
		}
	}
	private void createGadgetTagMap() {
		ArrayList<S3Resource.TagMap> map = null;
		if( gadgetArray != null ) {
			int n = gadgetArray.size();
			if( n > 0 ) {
				map = new ArrayList<S3Resource.TagMap>();
				int i;
				for( i = 0; i < n; i++ ) {
					S3Gadget g = gadgetArray.get(i);
					createGadgetTagMap(map, g);
				}
				if( map.isEmpty() ) {
					map = null;
				}
			}
		}
		gadgetTagMap = map;
	}
	private void addGadgetTagMap(S3Gadget g) {
		List<S3Resource.TagMap> map = null;
		if( gadgetTagMap == null )
			gadgetTagMap = new ArrayList<S3Resource.TagMap>();
		createGadgetTagMap(map, g);
		if( gadgetTagMap.isEmpty() )
			gadgetTagMap = null;
	}
	private void removeGadgetTagMap(S3Gadget g, String tag) {
		if( gadgetTagMap ==  null )
			return;
		int n = gadgetTagMap.size();
		int i;
		for( i = 0; i < n; i++ ) {
			TagMap tm = gadgetTagMap.get(i);
			if( tm.tag.equals(tag) ) {
				tm.removeGadget(g);
				if( tm.getCount() == 0 ) {
					gadgetTagMap.remove(i);
				}
			}
		}
		if( gadgetTagMap.isEmpty() ) {
			gadgetTagMap = null;
		}
	}
	private void removeGadgetTagMap(S3Gadget g) {
		if( gadgetTagMap ==  null )
			return;
		int n = gadgetTagMap.size();
		int i;
		for( i = 0; i < n; i++ ) {
			TagMap tm = gadgetTagMap.get(i);
			tm.removeGadget(g);
			if( tm.getCount() == 0 ) {
				gadgetTagMap.remove(i);
			}
		}
		if( gadgetTagMap.isEmpty() ) {
			gadgetTagMap = null;
		}
	}
	
	public int getGadgetCount(String tag) {
		if( gadgetArray == null ) {
			return 0;
		}
		if( gadgetTagMap == null ) {
			createGadgetTagMap();
		}
		if( gadgetTagMap == null ) {
			return 0;
		}
		TagMap map = findGadgetTagMap(gadgetTagMap, tag);
		if( map == null ) {
			return 0;
		}
		return map.getCount();
	}
	
	public String getGadgetId(String tag, int pos) {
		if( gadgetTagMap == null ) {
			return null;
		}
		TagMap map = findGadgetTagMap(gadgetTagMap, tag);
		if( map == null ) {
			return null;
		}
		if( pos < 0 || map.getCount() <= pos ) {
			return null;
		}
		return map.getGadget(pos).getId();
	}
	
	public S3Gadget findGadgetByTag(String tag, int position) {
		if( gadgetArray == null ) {
			return null;
		}
		if( gadgetTagMap == null ) {
			createGadgetTagMap();
		}
		if( gadgetTagMap == null ) {
			return null;
		}
		TagMap map = findGadgetTagMap(gadgetTagMap, tag);
		if( map == null ) {
			return null;
		}
		if( position < 0 || map.getCount() <= position )
			return null;
		return map.getGadget(position);
	}
	
	private void setGadget(S3Gadget g) throws S3Exception, S3TypeException {
		g.setModifyTime(new Date());
		if( gadgetArray == null) {
			gadgetArray = new ArrayList<S3Gadget>();
		}
		gadgetArray.add(g);
		if( g.getTags() != null ) {
			addGadgetTagMap(g);
		}
	}
	
	private void removeGadgetForReplace(String id) {
		S3Gadget g = null; 
		if( gadgetArray != null ) {
			int n = gadgetArray.size();
			int i;
			for( i = 0; i < n; i++ ) {
				g = gadgetArray.get(i);
				if( g.getId().equals(id) ) {
					if( g.getType() == SharePDF.GADGET_FILE ) {
						S3Context s3ctx = service.getContext();
						S3LocalStore lc = s3ctx.getLocalStore();
						try {
							lc.removeMetaData(service, this, g.getId());
						} catch (S3Exception e) {
							e.printStackTrace();
						}
					}
					gadgetArray.remove(i);
					break;
				}
			}
		}
		if( g != null ) {
			g.setRemoveTime(new Date());
			if( gadgetTagMap != null ) { 
				removeGadgetTagMap(g);
			}
		}
	}
	
	public synchronized void removeGadget(String id) throws S3Exception, S3TypeException {
		S3Gadget g = findGadget(id);
		if( g != null ) {
			if( g.getType() == SharePDF.GADGET_FILE ) {
				S3Context s3ctx = service.getContext();
				S3LocalStore lc = s3ctx.getLocalStore();
				try {
					lc.removeMetaData(service, this, g.getId());
				} catch (S3Exception e) {
					e.printStackTrace();
				}
			}
			if( gadgetTagMap != null ) { 
				removeGadgetTagMap(g);
			}
			if( g.getLastSyncTime() != null ) {
				g.setRemoveTime(new Date());
			} else {
				if( gadgetArray != null ) {
					int n = gadgetArray.size();
					int i;
					for( i = 0; i < n; i++ ) {
						S3Gadget rg = gadgetArray.get(i);
						if( rg == g ) {
							gadgetArray.remove(i);
							break;
						}
					}
				}
			}
			saveGadget();
		}
	}
	
	public void setGadgetTag(String id, String tag) throws S3Exception, S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null )
			throw new S3Exception("gadget not found");
		if( g.has(tag) ) return;
		g.setTag(tag);
		addGadgetTagMap(g, tag);
		saveGadget();
	}
	public void removeGadgetTag(String id, String tag) throws S3Exception, S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null )
			throw new S3Exception("gadget not found");
		if( !g.has(tag) ) return;
		removeGadgetTagMap(g, tag);
		g.removeTag(tag);
		saveGadget();
	}

	// integer gadget
	public int findIntGadget(String id) throws S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_INT )
			throw new S3TypeException("gadget type error");
		return g.getIntValue();
	}
	public void setGadget(String id, int v) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Gadget g = new S3Gadget(id, v);
		setGadget(g);
		saveGadget();
	}
	
	// float gadget
	public double findFloatGadget(String id) throws S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_FLOAT )
			throw new S3TypeException("gadget type error");
		return g.getFloatValue();
	}

	public void setGadget(String id, double v) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Gadget g = new S3Gadget(id, v);
		setGadget(g);
		saveGadget();
	}

	// Date gadget
	public Date findDateGadget(String id) throws S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_DATE )
			throw new S3TypeException("gadget type error");
		return g.getDateValue();
	}
	public void setGadget(String id, Date v) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Gadget g = new S3Gadget(id, v);
		setGadget(g);
		saveGadget();
	}

	// String gadget
	public String findStringGadget(String id) throws S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_STRING )
			throw new S3TypeException("gadget type error");
		return g.getStringValue();
	}
	public void setGadget(String id, String v) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Gadget g = new S3Gadget(id, v);
		setGadget(g);
		saveGadget();
	}

	// JSON gadget
	public JSONObject findJSONGadget(String id) throws S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_JSON )
			throw new S3TypeException("gadget type error");
		return g.getJSONValue();
	}
	public void setGadget(String id, JSONObject v) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Gadget g = new S3Gadget(id, v);
		setGadget(g);
		saveGadget();
	}
	
	// stream gadget
	public InputStream findStreamGadget(String id) throws S3Exception, S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_FILE )
			throw new S3TypeException("gadget type error");
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		return lc.openMetaData(service, this, id);
	}
	public File findStreamGadetFile(String id) throws S3Exception, S3TypeException {
		S3Gadget g = findGadget(id);
		if( g == null || g.getType() != SharePDF.GADGET_FILE )
			throw new S3TypeException("gadget type error");
		return g.getFileValue();
	}
	public void setGadget(String id, InputStream is) throws S3Exception, S3TypeException {
		removeGadgetForReplace(id);
		S3Context s3ctx = service.getContext();
		S3LocalStore lc = s3ctx.getLocalStore();
		File mf = lc.saveMetaData(service, this, id, is);
		S3Gadget g = new S3Gadget(id, mf);
		setGadget(g);
		saveGadget();
	}
}
