package railo.runtime.text.feed;

import java.util.Iterator;
import java.util.Map.Entry;

import railo.commons.lang.StringUtil;
import railo.runtime.exp.DatabaseException;
import railo.runtime.op.Caster;
import railo.runtime.type.Array;
import railo.runtime.type.CastableArray;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Query;
import railo.runtime.type.QueryImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.util.KeyConstants;

public class FeedQuery {

	public static final Collection.Key VERSION = KeyImpl.intern("VERSION");
	public static final Collection.Key ITEM = KeyImpl.intern("ITEM");
	public static final Collection.Key ENTRY = KeyImpl.intern("ENTRY");
	
	public static final Collection.Key AUTHOREMAIL = KeyImpl.intern("AUTHOREMAIL");
	public static final Collection.Key AUTHORNAME = KeyImpl.intern("AUTHORNAME");
	public static final Collection.Key AUTHORURI = KeyImpl.intern("AUTHORURI");
	public static final Collection.Key AUTHOR = KeyImpl.intern("AUTHOR");
	public static final Collection.Key CATEGORYLABEL = KeyImpl.intern("CATEGORYLABEL");
	public static final Collection.Key CATEGORYSCHEME = KeyImpl.intern("CATEGORYSCHEME");
	public static final Collection.Key CATEGORYTERM = KeyImpl.intern("CATEGORYTERM");
	public static final Collection.Key CATEGORY = KeyImpl.intern("CATEGORY");
	public static final Collection.Key COMMENTS = KeyImpl.intern("COMMENTS");
	public static final Collection.Key CONTENT = KeyImpl.intern("CONTENT");
	public static final Collection.Key CONTENTMODE = KeyImpl.intern("CONTENTMODE");
	public static final Collection.Key CONTENTSRC = KeyImpl.intern("CONTENTSRC");
	public static final Collection.Key CONTENTTYPE = KeyImpl.intern("CONTENTTYPE");
	public static final Collection.Key CONTRIBUTOREMAIL = KeyImpl.intern("CONTRIBUTOREMAIL");
	public static final Collection.Key CONTRIBUTORNAME = KeyImpl.intern("CONTRIBUTORNAME");
	public static final Collection.Key CONTRIBUTORURI = KeyImpl.intern("CONTRIBUTORURI");
	public static final Collection.Key CONTRIBUTOR = KeyImpl.intern("CONTRIBUTOR");
	public static final Collection.Key CREATEDDATE = KeyImpl.intern("CREATEDDATE");
	public static final Collection.Key CREATED = KeyImpl.intern("CREATED");
	public static final Collection.Key EXPIRATIONDATE = KeyImpl.intern("EXPIRATIONDATE");
	public static final Collection.Key ID = KeyImpl.intern("ID");
	public static final Collection.Key IDPERMALINK = KeyImpl.intern("IDPERMALINK");
	public static final Collection.Key LINKHREF = KeyImpl.intern("LINKHREF");
	public static final Collection.Key LINKHREFLANG = KeyImpl.intern("LINKHREFLANG");
	public static final Collection.Key LINKLENGTH = KeyImpl.intern("LINKLENGTH");
	public static final Collection.Key LINKREL = KeyImpl.intern("LINKREL");
	public static final Collection.Key LINKTITLE = KeyImpl.intern("LINKTITLE");
	public static final Collection.Key LINKTYPE = KeyImpl.intern("LINKTYPE");
	public static final Collection.Key PUBLISHEDDATE = KeyImpl.intern("PUBLISHEDDATE");
	public static final Collection.Key PUBLISHED = KeyImpl.intern("PUBLISHED");
	public static final Collection.Key PUBDATE = KeyImpl.intern("pubDate");
	public static final Collection.Key RDF_ABOUT = KeyImpl.intern("rdf:about");
	
	public static final Collection.Key RIGHTS = KeyImpl.intern("RIGHTS");
	public static final Collection.Key RSSLINK = KeyImpl.intern("RSSLINK");
	public static final Collection.Key SOURCE = KeyImpl.intern("SOURCE");
	public static final Collection.Key SOURCEURL = KeyImpl.intern("SOURCEURL");
	public static final Collection.Key SUMMARY = KeyImpl.intern("SUMMARY");
	public static final Collection.Key SUMMARYMODE = KeyImpl.intern("SUMMARYMODE");
	public static final Collection.Key SUMMARYSRC = KeyImpl.intern("SUMMARYSRC");
	public static final Collection.Key SUMMARYTYPE = KeyImpl.intern("SUMMARYTYPE");
	public static final Collection.Key TITLE = KeyImpl.intern("TITLE");
	public static final Collection.Key TITLETYPE = KeyImpl.intern("TITLETYPE");
	public static final Collection.Key UPDATEDDATE = KeyImpl.intern("UPDATEDDATE");
	public static final Collection.Key URI = KeyImpl.intern("URI");
	public static final Collection.Key XMLBASE = KeyImpl.intern("XMLBASE");
	public static final Collection.Key GUID = KeyImpl.intern("guid");
	public static final Collection.Key ENCLOSURE = KeyImpl.intern("enclosure");
	public static final Collection.Key LINK = KeyImpl.intern("link");
	public static final Collection.Key MODE = KeyImpl.intern("mode");
	public static final Collection.Key TEXT = KeyImpl.intern("text");
	public static final Collection.Key DOMAIN = KeyImpl.intern("domain");
	public static final Collection.Key ISSUED = KeyImpl.intern("issued");
	public static final Collection.Key COPYRIGHT = KeyImpl.intern("copyright");
	public static final Collection.Key SRC = KeyImpl.intern("src");
	public static final Collection.Key UPDATED = KeyImpl.intern("updated");
	public static final Collection.Key MODIFIED = KeyImpl.intern("modified");
	public static final Collection.Key URL = KeyImpl.intern("url");
	public static final Collection.Key LENGTH = KeyImpl.intern("length");
	public static final Collection.Key ISPERMALINK = KeyImpl.intern("isPermaLink");

	public static final Collection.Key DC_CONTRIBUTOR = KeyImpl.intern("DC_CONTRIBUTOR");
	public static final Collection.Key DC_COVERAGE = KeyImpl.intern("DC_COVERAGE");
	public static final Collection.Key DC_CREATOR = KeyImpl.intern("DC_CREATOR");
	public static final Collection.Key DC_DATE = KeyImpl.intern("DC_DATE");
	public static final Collection.Key DC_DESCRIPTION = KeyImpl.intern("DC_DESCRIPTION");
	public static final Collection.Key DC_FORMAT = KeyImpl.intern("DC_FORMAT");
	public static final Collection.Key DC_IDENTIFIER = KeyImpl.intern("DC_IDENTIFIER");
	public static final Collection.Key DC_LANGUAGE = KeyImpl.intern("DC_LANGUAGE");
	public static final Collection.Key DC_PUBLISHER = KeyImpl.intern("DC_PUBLISHER");
	public static final Collection.Key DC_RELATION = KeyImpl.intern("DC_RELATION");
	public static final Collection.Key DC_RIGHT = KeyImpl.intern("DC_RIGHTS");
	public static final Collection.Key DC_SOURCE = KeyImpl.intern("DC_SOURCE");
	public static final Collection.Key DC_TITLE = KeyImpl.intern("DC_TITLE");
	public static final Collection.Key DC_TYPE = KeyImpl.intern("DC_TYPE");
	
	public static final Collection.Key DC_SUBJECT_TAXONOMYURI = KeyImpl.intern("DC_SUBJECT_TAXONOMYURI");
	public static final Collection.Key DC_SUBJECT_VALUE = KeyImpl.intern("DC_SUBJECT_VALUE");
	public static final Collection.Key DC_SUBJECT = KeyImpl.intern("DC_SUBJECT");
	
  	  	  	  	
	
	
	
	private static Collection.Key[] COLUMNS=new Collection.Key[]{
		AUTHOREMAIL,
		AUTHORNAME,
		AUTHORURI,
		CATEGORYLABEL,
		CATEGORYSCHEME,
		CATEGORYTERM,
		COMMENTS,
		CONTENT,
		CONTENTMODE,
		CONTENTSRC,
		CONTENTTYPE,
		CONTRIBUTOREMAIL,
		CONTRIBUTORNAME,
		CONTRIBUTORURI,
		CREATEDDATE,
		EXPIRATIONDATE,
		ID,
		IDPERMALINK,
		LINKHREF,
		LINKHREFLANG,
		LINKLENGTH,
		LINKREL,
		LINKTITLE,
		LINKTYPE,
		PUBLISHEDDATE,
		RIGHTS,
		RSSLINK,
		SOURCE,
		SOURCEURL,
		SUMMARY,
		SUMMARYMODE,
		SUMMARYSRC,
		SUMMARYTYPE,
		TITLE,
		TITLETYPE,
		UPDATEDDATE,
		URI,
		XMLBASE
	};
	

	private static Collection.Key[] COLUMNS_WITH_DC=new Collection.Key[]{
		AUTHOREMAIL,
		AUTHORNAME,
		AUTHORURI,
		CATEGORYLABEL,
		CATEGORYSCHEME,
		CATEGORYTERM,
		COMMENTS,
		CONTENT,
		CONTENTMODE,
		CONTENTSRC,
		CONTENTTYPE,
		CONTRIBUTOREMAIL,
		CONTRIBUTORNAME,
		CONTRIBUTORURI,
		CREATEDDATE,
		
		DC_CONTRIBUTOR,
		DC_COVERAGE,
		DC_CREATOR,
		DC_DATE,
		DC_DESCRIPTION,
		DC_FORMAT,
		DC_IDENTIFIER,
		DC_LANGUAGE,
		DC_PUBLISHER,
		DC_RELATION,
		DC_RIGHT,
		DC_SOURCE,
		DC_TITLE,
		DC_TYPE,
		DC_SUBJECT_TAXONOMYURI,
		DC_SUBJECT_VALUE,
		
		EXPIRATIONDATE,
		ID,
		IDPERMALINK,
		LINKHREF,
		LINKHREFLANG,
		LINKLENGTH,
		LINKREL,
		LINKTITLE,
		LINKTYPE,
		PUBLISHEDDATE,
		RIGHTS,
		RSSLINK,
		SOURCE,
		SOURCEURL,
		SUMMARY,
		SUMMARYMODE,
		SUMMARYSRC,
		SUMMARYTYPE,
		TITLE,
		TITLETYPE,
		UPDATEDDATE,
		URI,
		XMLBASE
	};
	
	
	public static Query toQuery(Struct data,boolean hasDC) throws DatabaseException {
		Query qry=new QueryImpl(hasDC?COLUMNS_WITH_DC:COLUMNS,0,"");
		
		String version=Caster.toString(data.get(VERSION,""),"");
		Array items=null;
		if(StringUtil.startsWithIgnoreCase(version,"rss") || StringUtil.startsWithIgnoreCase(version,"rdf"))	{
			items=Caster.toArray(data.get(ITEM, null),null);
			if(items==null) {
				Struct sct=Caster.toStruct(data.get(version,null),null,false);
				if(sct!=null){
					items=Caster.toArray(sct.get(ITEM, null),null);
				}
			}
			return toQuery(true,qry,items);
		}
		else if(StringUtil.startsWithIgnoreCase(version,"atom"))	{
			items=Caster.toArray(data.get(ENTRY, null),null);
			return toQuery(false,qry,items);
		}
		return qry;
	}

	private static Query toQuery(boolean isRss,Query qry, Array items) {
		if(items==null)return qry;
		
		int len=items.size();
		Struct item;
		int row=0;
		Iterator<Entry<Key, Object>> it;
		Entry<Key, Object> e;
		for(int i=1;i<=len;i++) {
			item=Caster.toStruct(items.get(i, null),null,false);
			if(item==null) continue;
			qry.addRow();
			row++;
			it = item.entryIterator();
			while(it.hasNext()) {
				e = it.next();
				if(isRss)setQueryValueRSS(qry,e.getKey(),e.getValue(),row);
				else setQueryValueAtom(qry,e.getKey(),e.getValue(),row);
			}
			
		}
		
		return qry;
	}

	private static void setQueryValueAtom(Query qry, Key key, Object value, int row) {
		
		if(key.equals(AUTHOR)) {
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(AUTHOREMAIL, row, sct.get("email",null));
				qry.setAtEL(AUTHORNAME, row, sct.get("name",null));
				qry.setAtEL(AUTHORURI, row, sct.get("uri",null));
			}
		}
		if(key.equals(CATEGORY)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(CATEGORYLABEL, row, sct.get("label",null));
				qry.setAtEL(CATEGORYSCHEME, row, sct.get("scheme",null));
				qry.setAtEL(CATEGORYTERM, row, sct.get("term",null));
			}
			//else qry.setAtEL(CATEGORYLABEL, row, getValue(value));
		}
		else if(key.equals(COMMENTS)) {
			qry.setAtEL(COMMENTS, row, getValue(value));
		}
		else if(key.equals(CONTENT)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(CONTENT, row, getValue(sct));
				qry.setAtEL(CONTENTMODE, row, sct.get(MODE,null));
				qry.setAtEL(CONTENTSRC, row, sct.get(SRC,null));
				qry.setAtEL(CONTENTTYPE, row, sct.get(KeyConstants._type,null));
				qry.setAtEL(XMLBASE, row, sct.get("xml:base",null));
			}
			else qry.setAtEL(CONTENT, row, getValue(value));
		}
		else if(key.equals(CONTRIBUTOR)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(CONTRIBUTOREMAIL, row, sct.get("email",null));
				qry.setAtEL(CONTRIBUTORNAME, row, sct.get(KeyConstants._name,null));
				qry.setAtEL(CONTRIBUTORURI, row, sct.get("uri",null));
			}
		}
		else if(key.equals(CREATED)) {
			qry.setAtEL(CREATEDDATE, row, getValue(value));
		}
		else if(key.equals(ID)) {
			qry.setAtEL(ID, row, getValue(value));
		}
		else if(key.equals(LINK)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(LINKHREF, row, sct.get("href",null));
				qry.setAtEL(LINKHREFLANG, row, sct.get("hreflang",null));
				qry.setAtEL(LINKLENGTH, row, sct.get(LENGTH,null));
				qry.setAtEL(LINKREL, row, sct.get("rel",null));
				qry.setAtEL(LINKTITLE, row, sct.get(TITLE,null));
				qry.setAtEL(LINKTYPE, row, sct.get(KeyConstants._type,null));
			}
		}
		else if(key.equals(PUBLISHED)) {
			qry.setAtEL(PUBLISHEDDATE, row, getValue(value));
		}
		else if(key.equals(ISSUED)) {
			qry.setAtEL(PUBLISHEDDATE, row, getValue(value));
		}
		else if(key.equals(RIGHTS)) {
			qry.setAtEL(RIGHTS, row, getValue(value));
		}
		else if(key.equals(COPYRIGHT)) {
			qry.setAtEL(RIGHTS, row, getValue(value));
		}
		else if(key.equals(SUMMARY)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(SUMMARY, row, getValue(sct));
				qry.setAtEL(SUMMARYMODE, row, sct.get(MODE,null));
				qry.setAtEL(SUMMARYSRC, row, sct.get(SRC,null));
				qry.setAtEL(SUMMARYTYPE, row, sct.get(KeyConstants._type,null));
			}
			else qry.setAtEL(SUMMARY, row, getValue(value));
		}
		else if(key.equals(TITLE)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(TITLE, row, getValue(sct));
				qry.setAtEL(TITLETYPE, row, sct.get(KeyConstants._type,null));
			}
			else qry.setAtEL(TITLE, row, getValue(value));
		}
		else if(key.equals(UPDATED)) {
			qry.setAtEL(UPDATEDDATE, row, getValue(value));
		}
		else if(key.equals(MODIFIED)) {
			qry.setAtEL(UPDATEDDATE, row, getValue(value));
		}
	}
	private static void setQueryValueRSS(Query qry, Key key, Object value, int row) {
		
		if(key.equals(AUTHOR)) {
			qry.setAtEL(AUTHOREMAIL, row, getValue(value));
		}
		else if(key.equals(CATEGORY)) {
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(CATEGORYLABEL, row, getValue(sct));
				qry.setAtEL(CATEGORYSCHEME, row, sct.get(DOMAIN,null));
			}
			else qry.setAtEL(CATEGORYLABEL, row, getValue(value));
		}
		else if(key.equals(COMMENTS)) {
			qry.setAtEL(COMMENTS, row, getValue(value));
		}
		else if(key.equals(KeyConstants._description)) {
			qry.setAtEL(CONTENT, row, getValue(value));
		}
		else if(key.equals(EXPIRATIONDATE)) {
			qry.setAtEL(EXPIRATIONDATE, row, getValue(value));
		}
		else if(key.equals(GUID)) {
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(ID, row, getValue(sct));
				qry.setAtEL(IDPERMALINK, row, sct.get(ISPERMALINK,null));
			}
			else qry.setAtEL(ID, row, getValue(value));
		}
		else if(key.equals(ENCLOSURE)) {
			Struct sct=toStruct(value);
			if(sct!=null){
				qry.setAtEL(LINKHREF, row, sct.get(URL,null));
				qry.setAtEL(LINKLENGTH, row, sct.get(LENGTH,null));
				qry.setAtEL(LINKTYPE, row, sct.get(KeyConstants._type,null));
			}
		}
		else if(key.equals(PUBDATE)) {
			qry.setAtEL(PUBLISHEDDATE, row, getValue(value));
		}
		else if(key.equals(RDF_ABOUT)) {
			qry.setAtEL(URI, row, getValue(value));
		}
		else if(key.equals(LINK)) {
			
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(RSSLINK, row, getValue(sct));
				Object v = sct.get(RDF_ABOUT,null);
				if(v!=null) qry.setAtEL(URI, row, v);
			}
			else qry.setAtEL(RSSLINK, row, getValue(value));
		}
		else if(key.equals(SOURCE)) {
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(SOURCE, row, getValue(sct));
				qry.setAtEL(SOURCEURL, row, sct.get(URL,null));
			}
			else qry.setAtEL(SOURCE, row, getValue(value));
		}
		else if(key.equals(SUMMARY)) {
			Struct sct=toStruct(value);
			
			if(sct!=null){
				qry.setAtEL(SUMMARY, row, getValue(sct));
				qry.setAtEL(SUMMARYMODE, row, sct.get(MODE,null));
				qry.setAtEL(SUMMARYTYPE, row, sct.get(KeyConstants._type,null));
			}
			else qry.setAtEL(SUMMARY, row, getValue(value));
		}
		else if(key.equals(TITLE)) {
			qry.setAtEL(TITLE, row, getValue(value));
		}

		// Dublin Core
		if(key.getLowerString().startsWith("dc_")){
			
			if(key.equals(DC_CONTRIBUTOR)) {
				qry.setAtEL(DC_CONTRIBUTOR, row, getValue(value));
			}
			else if(key.equals(DC_COVERAGE)) {
				qry.setAtEL(DC_COVERAGE, row, getValue(value));
			}
			else if(key.equals(DC_CREATOR)) {
				qry.setAtEL(DC_CREATOR, row, getValue(value));
			}
			else if(key.equals(DC_DATE)) {
				qry.setAtEL(DC_DATE, row, getValue(value));
			}
			else if(key.equals(DC_DESCRIPTION)) {
				qry.setAtEL(DC_DESCRIPTION, row, getValue(value));
			}
			else if(key.equals(DC_FORMAT)) {
				qry.setAtEL(DC_FORMAT, row, getValue(value));
			}
			else if(key.equals(DC_IDENTIFIER)) {
				qry.setAtEL(DC_IDENTIFIER, row, getValue(value));
			}
			else if(key.equals(DC_LANGUAGE)) {
				qry.setAtEL(DC_LANGUAGE, row, getValue(value));
			}
			else if(key.equals(DC_PUBLISHER)) {
				qry.setAtEL(DC_PUBLISHER, row, getValue(value));
			}
			else if(key.equals(DC_RELATION)) {
				qry.setAtEL(DC_RELATION, row, getValue(value));
			}
			else if(key.equals(DC_RIGHT)) {
				qry.setAtEL(DC_RIGHT, row, getValue(value));
			}
			else if(key.equals(DC_SOURCE)) {
				qry.setAtEL(DC_SOURCE, row, getValue(value));
			}
			else if(key.equals(DC_SUBJECT_TAXONOMYURI)) {
				qry.setAtEL(DC_SUBJECT_TAXONOMYURI, row, getValue(value));
			}
			else if(key.equals(DC_SUBJECT)) {
				qry.setAtEL(DC_SUBJECT_VALUE, row, getValue(value));
			}
			else if(key.equals(DC_TITLE)) {
				qry.setAtEL(DC_TITLE, row, getValue(value));
			}
			else if(key.equals(DC_TYPE)) {
				qry.setAtEL(DC_TYPE, row, getValue(value));
			}
		}
		
	}


	public static Object getValue(Object value) {
		return getValue(value, false);
	}
	public static Object getValue(Object value,boolean includeChildren) {
		if(value instanceof Struct)return getValue((Struct)value,includeChildren);
		return Caster.toString(value,null);
	}

	public static Object getValue(Struct sct,boolean includeChildren) {
		Object obj = sct.get(KeyConstants._value, null);
		if(obj==null)obj=sct.get(TEXT,null);
		return obj;
	}

	private static Struct toStruct(Object value) {
		if(value instanceof Struct) return (Struct) value; 
		
		if(value instanceof Array)	{
			Struct sct=new StructImpl(),row;
			Array arr = (Array)value;
			int len=arr.size();
			//Key[] keys;
			Iterator<Entry<Key, Object>> it;
			Entry<Key, Object> e;
			String nw;
			Object ext;
			for(int i=1;i<=len;i++){
				row=Caster.toStruct(arr.get(i,null),null,false);
				if(row==null)continue;
				it = row.entryIterator();
				//keys = row.keys();
				while(it.hasNext()){
					e = it.next();
					ext=sct.get(e.getKey(),null);
					nw=Caster.toString(e.getValue(),null);
					if(nw!=null){
						if(ext==null) sct.setEL(e.getKey(), nw);
						else if(ext instanceof CastableArray){
							((CastableArray)ext).appendEL(nw);
						}
						else {
							CastableArray ca=new CastableArray();
							ca.appendEL(Caster.toString(ext,null));
							ca.appendEL(nw);
							sct.setEL(e.getKey(), ca);
						}
						
					}
				}
			}
			return sct;
		}
		
		
		return null;
	}

	public static Query toQuery(Query qry) {
		return qry;
	}
}
