/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package de.christofreichardt.jaxws.securitypriceservice;

import de.christofreichardt.diagnosis.AbstractTracer;
import de.christofreichardt.diagnosis.LogLevel;
import de.christofreichardt.diagnosis.Traceable;
import de.christofreichardt.diagnosis.TracerFactory;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

/**
 *
 * @author Christof Reichardt
 */
public class OnVistaExtractor implements PriceExtractorStrategy, Traceable {

  @Override
  public AbstractSecurityPrice extractPrice(String id) throws SecurityPriceServiceException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("AbstractSecurityPrice", this, "extractPrice(String id)");
    
    try {
      tracer.out().printfIndentln("id = %s", id);
      
      AbstractSecurityPrice securityPrice;
      try {
        Document document = Jsoup.connect("http://www.onvista.de/suche/" + id).timeout(25000).userAgent("Mozilla").get();
        SecurityType securityType = determineType(document);
        switch (securityType) {
          case SHARE:
            securityPrice = evaluateShare(document);
            break;
          case OPTION:
            securityPrice = evaluateOption(document);
            break;
          case INDEX_CERTIFICATE:
            securityPrice = evaluateIndex(document);
            break;
          default:
            securityPrice = handleUnknownSecurity(id);
            break;
        }
      
        return securityPrice;
      }
      catch (IOException ex) {
        tracer.logException(LogLevel.ERROR, ex, getClass(), "extractPrice(String id)");
        throw makeFault(ex.getMessage());
      }
    }
    finally {
      tracer.wayout();
    }
  }
  
  private SecurityType determineType(Document document) {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("SecurityType", this, "determineType(Document document)");
    
    try {
      SecurityType securityType = SecurityType.UNKNOWN;
      
      if (securityType == SecurityType.UNKNOWN) {
        Element term = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT article div.WERTPAPIER span.WERTPAPIERNAME a.INSTRUMENT span.INSTRUMENT.AKTIE").first();
        if (term != null  &&  "Aktie".equals(term.text()))
          securityType = SecurityType.SHARE;
      }
      
      if (securityType == SecurityType.UNKNOWN) {
        Element category = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT span.skb").first();
        if (category != null &&  category.text().startsWith("Kategorie: Standard Optionsscheine"))
          securityType = SecurityType.OPTION;
      }
      
      if (securityType == SecurityType.UNKNOWN) {
        Element category = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT span.skb").first();
        if (category != null &&  category.text().startsWith("Kategorie: Partizipations- und Indexzertifikate"))
          securityType = SecurityType.INDEX_CERTIFICATE;
      }
      
      return securityType;
    }
    finally {
      tracer.wayout();
    }
  }
  
  private UnknownPrice handleUnknownSecurity(String id) {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("UnknownPrice", this, "handleUnknownSecurity(String id)");
    
    try {
      ObjectFactory objectFactory = new ObjectFactory();
      Security security = objectFactory.createSecurity();
      if (id.length() == 6)
        security.setWKN(id);
      else if (id.length() == 12)
        security.setISIN(id);
      UnknownPrice unknownPrice = objectFactory.createUnknownPrice();
//      UnknownPrice unknownPrice = new MyUnknownPrice();
      unknownPrice.setSecurity(security);
      
      return unknownPrice;
    }
    finally {
      tracer.wayout();
    }
  }
  
  private SharePrice evaluateShare(Document document) throws SecurityPriceServiceException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("SharePrice", this, "evaluateShare(Document document)");
    
    try {
      ObjectFactory objectFactory = new ObjectFactory();
      Security security = objectFactory.createSecurity();
      SharePrice sharePrice = objectFactory.createSharePrice();
//      SharePrice sharePrice = new MySharePrice();
      sharePrice.setSecurity(security);
      
      Elements elements = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT article div.WERTPAPIER_DETAILS dl dd");
      if(elements.size() != 6)
        throw makeFault("Unexpected page layout.");
      Element wkn = elements.first();
      Element isin = elements.get(1);
      Element anchor = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT article div.WERTPAPIER span.WERTPAPIERNAME a.INSTRUMENT").first();
      if (anchor == null)
        throw makeFault("Unexpected page layout.");
      Element rate = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT article ul.KURSDATEN li span").first();
      if (rate == null)
        throw makeFault("Unexpected page layout.");
      
      tracer.out().printfIndentln("wkn.text() = %s", wkn.text());
      tracer.out().printfIndentln("isin.text() = %s", isin.text());
      tracer.out().printfIndentln("anchor.text() = %s", anchor.text());
      tracer.out().printfIndentln("rate.text() = %s", rate.text());
      
      security.setISIN(isin.text());
      security.setWKN(wkn.text().substring(0, 6));
      security.setName(anchor.text());
      sharePrice.setRate(new BigDecimal(rate.text().replace(".", "").replace(",", ".")));
      
      return sharePrice;
    }
    finally {
      tracer.wayout();
    }
  }
  
  private OptionPrice evaluateOption(Document document) throws SecurityPriceServiceException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("OptionPrice", this, "evaluateOption(Document document)");
    
    try {
      ObjectFactory objectFactory = new ObjectFactory();
      Security security = objectFactory.createSecurity();
      OptionPrice optionPrice = objectFactory.createOptionPrice();
//      OptionPrice optionPrice = new MyOptionPrice();
      optionPrice.setSecurity(security);
      
      Element h1 = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT h1.EINZELANSICHTEN").first();
      if (h1 == null)
        throw makeFault("Unexpected page layout.");
      String securityName = h1.text();
      
      Elements tds = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.t table.weiss.abst tbody tr.hgrau2 td");
      if (tds.size() < 15)
        throw makeFault("Unexpected page layout.");
      String wkn = tds.get(1).text();
      String isin = tds.get(3).text();
      String strikePrice = tds.get(11).text();
      
      Elements tables = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.t table.weiss.abst");
      if (tables.size() < 3)
        throw makeFault("Unexpected page layout.");
      Element ratesTable = tables.get(2);
      Elements spans = ratesTable.select("tbody tr td span");
      if (spans.size() < 4)
        throw makeFault("Unexpected page layout.");
      String bid = spans.get(1).text();
      String ask = spans.get(3).text();
      
      Element table = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.hp_50_r div.t table.weiss").last();
      if (table == null)
        throw makeFault("Unexpected page layout.");
      tds = table.select("tbody tr td");
      if (tds.size() < 16)
        throw makeFault("Unexpected page layout.");
      String expirationDate = tds.get(11).text();
      String exercise = tds.get(15).text();
      
      tracer.out().printfIndentln("securityName = %s", securityName);
      tracer.out().printfIndentln("wkn = %s", wkn);
      tracer.out().printfIndentln("isin = %s", isin);
      tracer.out().printfIndentln("strikePrice = %s", strikePrice);
      tracer.out().printfIndentln("bid = %s", bid);
      tracer.out().printfIndentln("ask = %s", ask);
      tracer.out().printfIndentln("expirationDate = %s", expirationDate);
      tracer.out().printfIndentln("StringUtil.trim(expirationDate) = %s", StringUtil.trim(expirationDate));
      tracer.out().printfIndentln("exercise = %s", exercise);
      
      security.setISIN(isin);
      security.setWKN(wkn);
      security.setName(securityName);
      optionPrice.setExercise("Amerikanisch".equalsIgnoreCase(exercise) ? Exercise.AMERICAN : Exercise.EUROPEAN);
      optionPrice.setStrikePrice(new BigDecimal(strikePrice.replace(".", "").replace(",", ".")));
      if (!"n.a.".equals(bid))
        optionPrice.setBid(new BigDecimal(bid.replace(".", "").replace(",", ".")));
      if (!"n.a.".equals(ask))
        optionPrice.setAsk(new BigDecimal(ask.replace(".", "").replace(",", ".")));
      Scanner scanner = new Scanner(StringUtil.trim(expirationDate));
      scanner.useDelimiter("\\.");
      try {
        int day = scanner.nextInt();
        int month = scanner.nextInt();
        String year = scanner.next();
        XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance().
            newXMLGregorianCalendar(new BigInteger(year), month, day, 0, 0, 0, new BigDecimal(0), 0);
        optionPrice.setExpirationDate(xmlGregorianCalendar);
        
        tracer.out().printfIndentln("xmlGregorianCalendarDate = %s", xmlGregorianCalendar);
      }
      catch (DatatypeConfigurationException ex) {
        throw makeFault(ex.getMessage());
      }
      
      return optionPrice;
    }
    finally {
      tracer.wayout();
    }
  }
  
  private IndexPrice evaluateIndex(Document document) throws SecurityPriceServiceException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("IndexPrice", this, "evaluateIndex(Document document)");
    
    try {
      ObjectFactory objectFactory = new ObjectFactory();
      Security security = objectFactory.createSecurity();
      IndexPrice indexPrice = objectFactory.createIndexPrice();
//      IndexPrice indexPrice = new MyIndexPrice();
      indexPrice.setSecurity(security);
      
      Element td = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.t table.weiss.abst tbody tr td.s2b").first();
      if (td == null)
        throw makeFault("Unexpected page layout.");
      Element span = td.select("span").last();
      if (span == null)
        throw makeFault("Unexpected page layout.");
      String bid = span.text();
      
      td = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.t table.weiss.abst tbody tr td.s2b").last();
      if (td == null)
        throw makeFault("Unexpected page layout.");
      span = td.select("span").last();
      if (span == null)
        throw makeFault("Unexpected page layout.");
      String ask = td.select("span").last().text();
      
      Element a = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.hp_50_r div.t table.weiss.abst tbody tr td a.pfeil").first();
      if (a == null)
        throw makeFault("Unexpected page layout.");
      String issuer = a.text();
      
      Element h1 = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT h1.EINZELANSICHTEN").first();
      if (h1 == null)
        throw makeFault("Unexpected page layout.");
      String name = h1.attr("title");
      
      Elements tds = document.select("div#ONVISTA div.WEBSEITE div.CONTAINER1 div.INHALT div.t table.weiss.abst tbody tr.hgrau2 td");
      if (tds.size() < 4)
        throw makeFault("Unexpected page layout.");
      String wkn = tds.get(1).text();
      String isin = tds.get(3).text();
      
      tracer.out().printfIndentln("bid = %s", bid);
      tracer.out().printfIndentln("ask = %s", ask);
      tracer.out().printfIndentln("issuer = %s", issuer);
      tracer.out().printfIndentln("name = %s", name);
      tracer.out().printfIndentln("wkn = %s", wkn);
      tracer.out().printfIndentln("isin = %s", isin);
      
      security.setName(name);
      security.setWKN(wkn);
      security.setISIN(isin);
      if (!"n.a.".equals(bid)) 
        indexPrice.setBid(new BigDecimal(bid.replace(".", "").replace(",", ".")));
      if (!"n.a.".equals(ask))
        indexPrice.setAsk(new BigDecimal(ask.replace(".", "").replace(",", ".")));
      indexPrice.setIssuer(issuer);
       
      return indexPrice;
    }
    finally {
      tracer.wayout();
    }
  }
  
//  private void checkPageLayout(Element element) throws SecurityPriceServiceException {
//    AbstractTracer tracer = getCurrentTracer();
//    tracer.entry("void", this, "checkPageLayout(Element element)");
//    
//    try {
//      if (element == null) {
//        SecurityPriceServiceFault securityPriceServiceFault = new SecurityPriceServiceFault();
//        securityPriceServiceFault.setMessage("Unexpected page layout.");
//        throw new SecurityPriceServiceException(securityPriceServiceFault);
//      }
//    }
//    finally {
//      tracer.wayout();
//    }
//  }
  
  private SecurityPriceServiceException makeFault(String message) {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("void", this, "throwFault(String message)");
    
    try {
      SecurityPriceServiceFault securityPriceServiceFault = new SecurityPriceServiceFault();
      securityPriceServiceFault.setMessage(message);
      return new MySecurityPriceServiceException(securityPriceServiceFault);
    }
    finally {
      tracer.wayout();
    }
  }

  @Override
  public AbstractTracer getCurrentTracer() {
    return TracerFactory.getInstance().getCurrentQueueTracer();
  }

}
