package kz.goodssales.GoodsSales.dbms;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.ServletContextAware;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import tctable.Tools;
import tools.User;
import tools.XMLTools;

@Controller
@SessionAttributes( { "user" }) //Сесионный объект!
public class DBMSTree implements ServletContextAware  {
	
	private static final Logger logger = LoggerFactory.getLogger(kz.goodssales.GoodsSales.dbms.DBMSTree.class);
	private ServletContext context;

	//If not created object "user", create him.
	@ModelAttribute("user")
	public User populatePerson() {
		return new User("none");
	}
	
    @RequestMapping(value = "/tree",method = RequestMethod.POST,produces = "application/xml; charset=utf-8")
    @ResponseBody
    public Object ajaxTamer(@ModelAttribute User user,@RequestBody byte[] reqData,@RequestParam(required=false,name="lng") String language_id) {
    	
    	if(language_id!=null && !language_id.equals("")) 
    		user.language_id=language_id;
    	
    	logger.info("user.id="+user.id+" user.name="+user.name+" user.language_id="+user.language_id+" user.country_id="+user.country_id);
    	
    	boolean error=false;
    	String result="<metadata fn=\"-1\"><![CDATA[Request not processed!]]></metadata>";
    	
    	String jspPath = context.getRealPath("/");
		String db_url = "";
		String db_login = "";
		String db_password = "";

		//Load DB configuration from "config.xml"
		try {
			//String fullPath = context.getRealPath("/WEB-INF/config.xml");
			//File fXmlFile = new File(fullPath);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			//Document doc = dBuilder.parse(fXmlFile);
			Document doc = dBuilder.parse(new ClassPathResource("config.xml").getInputStream());
			Element nMain = doc.getDocumentElement();
			NodeList nl = nMain.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				if (nl.item(i).getNodeName().equals("db-url"))
					db_url = nl.item(i).getTextContent();
				if (nl.item(i).getNodeName().equals("db-login"))
					db_login = nl.item(i).getTextContent();
				if (nl.item(i).getNodeName().equals("db-password"))
					db_password = nl.item(i).getTextContent();
			}
		} catch (Exception ex) {
			logger.info(ex.getMessage());
		}

		Connection conn = null;
		try {
			Class.forName("org.postgresql.Driver");
			conn = DriverManager.getConnection(db_url, db_login, db_password);
			if (conn != null) {
				logger.info("Connect is OK!");
			} else {
		    	error=true;
		    	result="<metadata fn=\"-1\"><![CDATA[An error occurred while connecting to the database!]]></metadata>";
			}
		} catch (Exception ex) {
			logger.info(ex.getMessage());
	    	error=true;
	    	result="<metadata fn=\"-1\"><![CDATA[An error occurred while connecting to the database!]]></metadata>";
		}
		
		String fn="";
		String treeid="";
		String htmlid="";
		
		//Парсим принятый XML запрос
    	InputStream body = new ByteArrayInputStream(reqData);
		Document doc = null;
		Element reqNode = null;
		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			doc = dBuilder.parse(body);
		} catch (Exception ex) {
			logger.info(ex.getMessage());
			return "<metadata fn=\"-1\"><![CDATA[Parsing request error!]]></metadata>";
		}
		if (doc != null) {
			reqNode = doc.getDocumentElement();
		}

		
		//Парсим XML из файла
		Document objXMLDocument = null;
		try {
			File inputFile = new File(jspPath+"resources"+File.separator+"engine/tree.xml");
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			objXMLDocument = dBuilder.parse(inputFile);
		} catch (Exception ex) {
			logger.info(ex.getMessage());
			error=true;
		}		
		
		
		Node mainNode=null;
		//находим нужный узел в tree.xml для того чтобы выполнить запрос
		XPathFactory xPathfactory = XPathFactory.newInstance();
		XPath xpath = xPathfactory.newXPath();
		
		if (doc != null) {
			Object exprResult=null;
			try {
				XPathExpression expr = xpath.compile("//metadata/type[@id='" + treeid + "']");
				exprResult = expr.evaluate(doc, XPathConstants.NODESET);
			} catch (XPathExpressionException ex) {
				logger.info(ex.getMessage());
			}
			NodeList nodeList = (NodeList) exprResult;

			if (nodeList.getLength() > 0)
				mainNode = nodeList.item(0);
		}

		String retrez="";
		if(mainNode!=null)
		{
			//перебераем все дочерние элементы и для каждого выполняем запрос c фильтрацией
			Node currNode = mainNode.getFirstChild(); //из tree.xml
			while (currNode != null)
			{

				Node tmpNode=currNode; //если узел goto

				if (tmpNode.getNodeName().equals("goto")) //если встретилась "зацикливалка"
				{
					treeid = tmpNode.getAttributes().getNamedItem("id").getNodeValue();
					tmpNode=XMLTools.findFirstNodeOnAttribute(objXMLDocument.getDocumentElement(),"type","id",treeid);
					if(tmpNode==null) { currNode = currNode.getNextSibling(); continue; }
				}

				if(tmpNode.getNodeName().equals("type")) //если выборка из базы
				{
					treeid=tmpNode.getAttributes().getNamedItem("id").getNodeValue();
					String caption=tmpNode.getAttributes().getNamedItem("c").getNodeValue();

					//j=0;
					XMLTools.applyNodeToNode(reqNode,tmpNode,"n");

					//Переносим значения в SQL запрос из фильтра
					String sql=XMLTools.getCDATAValue(XMLTools.findNode(tmpNode,"sql-query"));
					Node nFs=XMLTools.findNode(tmpNode, "filter");
					if(nFs!=null)
					{	
						Node nF=nFs.getFirstChild();
						while(nF != null)
						{
							if(nF.getNodeName().equals("column"))
							{
								String vt = nF.getAttributes().getNamedItem("vt").getNodeValue();
								String val= XMLTools.getCDATAValue(nF);									
								sql = sql.replace("${" + nF.getAttributes().getNamedItem("n").getNodeValue() + "}", Tools.getSQLValue(vt, val));
							}
							nF=nF.getNextSibling();
						}
					}

					//Выполняем подготовленный SQL
					Statement stmt;
					ResultSet rs=null;
					try {
						stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
						rs = stmt.executeQuery(sql);
					} catch (SQLException e) {
						e.printStackTrace();
					}				

				
					//res=fnGetData(reqNode,tmpNode);//currNode из tree.xml
					if(rs==null)
					{
						//sendError('fnGetData==null!');
					}else
					{
						try {
							while (rs.next()) //while (row = res->fetch(PDO::FETCH_ASSOC))
							{
								String fid="";
								String iid="";
								String val="";

								if(Tools.hasColumn(rs,"id")) fid=rs.getString("id"); else fid=""; //Уникальный id записи
								if(Tools.hasColumn(rs,"icon_id")) iid=rs.getString("icon_id"); else iid=""; //id значка
								if(Tools.hasColumn(rs,caption)) val=rs.getString(caption); else val=""; //Заголовок

								String visible = "";
								if(tmpNode.getAttributes().getNamedItem("visible").getNodeValue().equals("0")) visible=" visible=\"0\" ";
								//Для проверки есть ли дети составляем XML запрос и отправляем в вункцию как будто он пришел от клиента
								//c - Есть ли под узлы по умолчанию есть
								//fid - id записи
								//iid - id иконки
								//treeid - id ветки дерева
								//ObjectID - название поля с уникальным идентификатором записи
								String xmlnode = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
								xmlnode+="<tree c=\"1\" fid=\""+fid+"\" iid=\""+iid+"\" treeid=\""+treeid+"\" t=\""+tmpNode.getAttributes().getNamedItem("n").getNodeValue()+"\" ObjectID=\""+tmpNode.getAttributes().getNamedItem("ObjectID").getNodeValue()+"\""+visible+">";
								xmlnode+="<![CDATA["+val+"]]>";
								//сохраняем параметры фильтра для дочерних элементов с текщем состоянием
								//перебираем фильтры которые должны быть заполненны для каждого узла даные для фильтра беруться из результ сета
								xmlnode+="<columns>";

								//считываем название поля и находим данные в результсете
								Node nodeParam = XMLTools.findFirstNode(tmpNode, "columns"); //tree.xml
								if(nodeParam!=null) nodeParam=nodeParam.getFirstChild();
								while (nodeParam != null)
								{
									if(nodeParam.getNodeName().equals("param"))
									{
										String fname = nodeParam.getAttributes().getNamedItem("n").getNodeValue();
										String fval="";
										try
										{
											if(Tools.hasColumn(rs,fname))
											{
												fval=rs.getString(fname);
											}else
											{	fval=XMLTools.getCDATAValue(nodeParam);
											}
										} catch (Exception e)
										{
											//sendError(e->getMessage());
										}
										xmlnode+="<param n=\""+fname+"\"><![CDATA["+fval+"]]></param>";
									}
									nodeParam = nodeParam.getNextSibling();
								}
								xmlnode+="</columns>";
								xmlnode+="</tree>";

								//парсим созданную ветку дерева в DOMDocument потом посылаем в функцию взятия данных как будто их все открыли
								//если есть данные то у этого узла дерева есть дети c="1" инече нет c="0".
								int child = 0;
								Document objXMLDocTree=null;
								try
								{	//objXMLDocTree->loadXML(xmlnode);
									DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
									DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
									objXMLDocument = dBuilder.parse(xmlnode);
								} catch (Exception e)
								{	//sendError(e->getMessage());
								}
								Element testNodeTree = objXMLDocTree.getDocumentElement();

								Node testNode = tmpNode.getFirstChild(); //Текущий узел из tree.xml
								while (testNode != null)
								{
									Node tmpNode2 = testNode;
									if(tmpNode2.getNodeName().equals("goto"))
									{
										treeid=tmpNode2.getAttributes().getNamedItem("id").getNodeValue();
										tmpNode2=XMLTools.findFirstNodeOnAttribute(objXMLDocument.getDocumentElement(),"type","id",treeid);
										if(tmpNode2==null) { testNode = testNode.getNextSibling(); continue; }
									}
									if(tmpNode2.getNodeName().equals("type"))
									{
										/*Object testrs = fnGetData(conn,testNodeTree,tmpNode2);
										if((testrs!=null)&&(testrs.rowCount()>0))
										{
											child=1;
											break;																								
										}*/
									}
									testNode = testNode.getNextSibling();
								}
								//testNodeTree.getAttribute("c",child);

								//retrez+=objXMLDocTree->saveXML(objXMLDocTree.getDocumentElement());
								//Закончили проверку на детей

							}
						} catch (DOMException | SQLException e) {
							e.printStackTrace();
						}
						try {
							rs.close();
						} catch (SQLException e) {
							e.printStackTrace();
						}

					}
				}

				currNode = currNode.getNextSibling();
			}
		}else
		{
			result="<metadata fn=\"-1\"><![CDATA[Not find find id=\""+treeid+"\"!]]></metadata>";
		}

		result="<?xml version=\"1.0\" encoding=\"utf-8\"?><metadata fn=\"1\" htmlid=\""+htmlid+"\">"+retrez+"</metadata>";
		//header('Content-type: text/xml');
	
		
		
		try {
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		//return body content
    	return result;
    }	
	
	//Replace all the values of the first filter values from the second
	public void setFilter(Node n1, Node n2) {
		if (n1 == null || n2 == null)
			return;

		XPathFactory xPathfactory = XPathFactory.newInstance();
		XPath xpath = xPathfactory.newXPath();

		Node nc1 = n1.getFirstChild();
		while (nc1 != null) {
			if (nc1.getNodeName().equals("column")) {
				try {
					String path = "column[@n='" + nc1.getAttributes().getNamedItem("n").getNodeValue() + "']";
					XPathExpression expr = xpath.compile(path);
					NodeList nodeList = (NodeList) expr.evaluate(n2, XPathConstants.NODESET);
					if (nodeList.getLength() > 0) {
						Node nc2 = nodeList.item(0);

						XMLTools.setCharacterDataToElement((Element) nc1, XMLTools.getCharacterDataFromElement((Element) nc2));

						//getCdata($nc1)->nodeValue=getCdata($nc2)->nodeValue;	
					}

				} catch (Exception ex) {
					System.out.println(ex.getMessage());
					//String message = "XML parsing error!";
					//return;
				}
			}
			nc1 = nc1.getNextSibling();
		}
	}
	
	
	public ResultSet fnGetData(Connection conn,Node treeNode,Node currNode)
	{
		String sql=getSQL(treeNode,currNode);

		/*if(gettype($_SESSION['USER_ID'])=='string')
			sql=str_replace('${_user_id}',$_SESSION['USER_ID']=='' ? 'null' : '\''.$_SESSION['USER_ID'].'\'',$sql);
		else
			sql=str_replace('${_user_id}',$_SESSION['USER_ID']=='' ? 'null' : $_SESSION['USER_ID'],$sql);*/
		
		Statement stmt;
		ResultSet rs=null;
		try {
			stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
			rs = stmt.executeQuery(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
	
	/** Перенести параметры из родительского в sql строку дочернего элемента
	 * @param XMLNode $nParent Родительский узел
	 * @param XMLNode $nChild Дочерний узел
	 * @result Строка
	 */
	public String getSQL(Node nParent,Node nChild)
	{
		if(nChild==null) return "";
		String sql="";
		
		Node nPs=XMLTools.findNode(nParent, "columns");
		Node nFs=XMLTools.findNode(nChild, "filter");
		//Переносим значения в фильтр
		if(nFs!=null)
		{
			Node nP;
			if(nPs!=null) nP=nPs.getFirstChild(); else nP=null;
			while (nP != null)
			{
				if (nP.getNodeName().equals("param"))
				{
					String val=XMLTools.getCDATAValue(nP);

					Node nF=XMLTools.findNodeOnAttribute(nFs, "column", "pn", nP.getAttributes().getNamedItem("n").getNodeValue());
					if(nF!=null)
						XMLTools.setCharacterDataToElement(nF, val);
				}
				nP = nP.getNextSibling();
			}
		}
		//Переносим значения в SQL запрос из фильтра
		sql=XMLTools.getCDATAValue(XMLTools.findNode(nChild,"sql-query"));
		nFs=XMLTools.findNode(nChild, "filter");
		if(nFs!=null)
		{	Node nF = nFs.getFirstChild();
			while(nF != null)
			{
				if(nF.getNodeName().equals("column"))
				{
					sql = sql.replace("{"+nF.getAttributes().getNamedItem("n").getNodeValue()+"}", Tools.getSQLValue(nF.getAttributes().getNamedItem("vt").getNodeValue(),XMLTools.getCDATAValue(nF)));					
				}
				nF=nF.getNextSibling();
			}
		}
		return sql;
	}
	
	@Override
	public void setServletContext(ServletContext servletContext) {
		this.context=servletContext;
	}

}
