/*******************************************************************************
 * Copyright (c) 2010 Robert "Unlogic" Olofsson (unlogic@unlogic.se).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0-standalone.html
 ******************************************************************************/
package se.unlogic.standardutils.settings;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import se.unlogic.standardutils.numbers.NumberUtils;
import se.unlogic.standardutils.xml.XMLUtils;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class XMLSettingNode implements SettingNode {

   private final Element element;
   private final XPath xpath;


   public XMLSettingNode(String path) throws SAXException, IOException, ParserConfigurationException {

      Document doc = XMLUtils.parseXmlFile(path, false,false);
      this.element = doc.getDocumentElement();

      this.xpath = XPathFactory.newInstance().newXPath();

   }

   public XMLSettingNode(Document doc) {

      this.element = doc.getDocumentElement();

      this.xpath = XPathFactory.newInstance().newXPath();
   }

   public XMLSettingNode(Element element) {

      this.element = element;

      this.xpath = XPathFactory.newInstance().newXPath();

   }

   public XMLSettingNode(File configurationFile) throws SAXException, IOException, ParserConfigurationException {

      Document doc = XMLUtils.parseXmlFile(configurationFile, false);
      this.element = doc.getDocumentElement();

      this.xpath = XPathFactory.newInstance().newXPath();
   }

   public Boolean getBoolean(String expression) {

      return Boolean.valueOf(this.getString(expression));

   }

   public boolean getPrimitiveBoolean(String expression) {

      return Boolean.parseBoolean(this.getString(expression));

   }

   public Double getDouble(String expression) {

      String value = this.getString(expression);

      if(value != null){
         return NumberUtils.toDouble(value);
      }

      return null;

   }

   public List<Double> getDoubles(String expression) {

      NodeList nodes = this.getNodeList(expression);

      List<Double> doubles = new ArrayList<Double>();

      for (int i = 0; i < nodes.getLength(); i++) {

         String value = nodes.item(i).getTextContent();

         if(value != null){
            Double numberValue = NumberUtils.toDouble(value);

            if(numberValue != null){
               doubles.add(numberValue);
            }
         }
      }

      return doubles;

   }

   public int getInt(String expression) {

      String value = this.getString(expression);

      if(value != null && NumberUtils.isInt(value)){
         return NumberUtils.toInt(value);
      }

      return 0;

   }

   public Integer getInteger(String expression) {

      String value = this.getString(expression);

      if(value != null){
         return NumberUtils.toInt(value);
      }

      return null;

   }

   public List<Integer> getIntegers(String expression) {

      NodeList nodes = this.getNodeList(expression);

      List<Integer> integers = new ArrayList<Integer>();

      for (int i = 0; i < nodes.getLength(); i++) {

         String value = nodes.item(i).getTextContent();

         if(value != null){
            Integer numberValue = NumberUtils.toInt(value);

            if(numberValue != null){
               integers.add(numberValue);
            }
         }
      }

      return integers;

   }

   public Long getLong(String expression) {

      String value = this.getString(expression);

      if(value != null){
         return NumberUtils.toLong(value);
      }

      return null;

   }

   public List<Long> getLongs(String expression) {

      NodeList nodes = this.getNodeList(expression);

      List<Long> longs = new ArrayList<Long>();

      for (int i = 0; i < nodes.getLength(); i++) {

         String value = nodes.item(i).getTextContent();

         if(value != null){
            Long numberValue = NumberUtils.toLong(value);

            if(numberValue != null){
               longs.add(numberValue);
            }
         }
      }

      return longs;

   }

   public XMLSettingNode getSetting(String expression) {

      Element element = this.getElement(expression);

      if(element == null){

         return null;
      }

      return new XMLSettingNode(element);

   }

   public List<XMLSettingNode> getSettings(String expression) {

      NodeList nodes = this.getNodeList(expression);

      List<XMLSettingNode> settingNodes = new ArrayList<XMLSettingNode>();

      for(int i = 0; i < nodes.getLength(); i++){

         settingNodes.add(new XMLSettingNode((Element)nodes.item(i)));

      }

      return settingNodes;

   }

   public String getString(String expression) {

      try {
         return this.xpath.evaluate(expression, this.element);
      } catch (XPathExpressionException e) {
         return null;
      }

   }

   public List<String> getStrings(String expression) {

      NodeList nodes = this.getNodeList(expression);

      if(nodes != null && nodes.getLength() > 0){

         List<String> strings = new ArrayList<String>();

         for (int i = 0; i < nodes.getLength(); i++) {

            strings.add(nodes.item(i).getTextContent());

         }

         return strings;
      }

      return null;
   }

   private NodeList getNodeList(String expression){

      try {
         return (NodeList) this.xpath.evaluate(expression, this.element, XPathConstants.NODESET);
      } catch(XPathExpressionException e) {
         return null;
      }

   }

   private Element getElement(String expression){

      try {
         return (Element) this.xpath.evaluate(expression, this.element, XPathConstants.NODE);
      } catch(XPathExpressionException e) {
         return null;
      }
   }

   public String getElementName(){

      return element.getTagName();
   }
}
