package org.openbravo.connector.bmSales;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.hibernate.criterion.Expression;
import org.openbravo.base.provider.OBProvider;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.database.ConnectionProvider;
import org.openbravo.erpCommon.businessUtility.Tax;
import org.openbravo.erpCommon.utility.Utility;
import org.openbravo.model.ad.access.User;
import org.openbravo.model.ad.process.ProcessInstance;
import org.openbravo.model.ad.system.Client;
import org.openbravo.model.ad.ui.Process;
import org.openbravo.model.common.businesspartner.BusinessPartner;
import org.openbravo.model.common.enterprise.DocumentType;
import org.openbravo.model.common.enterprise.Organization;
import org.openbravo.model.common.enterprise.Warehouse;
import org.openbravo.model.common.order.Order;
import org.openbravo.model.common.order.OrderLine;
import org.openbravo.model.common.plm.Product;
import org.openbravo.model.financialmgmt.tax.TaxRate;
import org.openbravo.scheduling.ProcessBundle;
import org.openbravo.scheduling.ProcessLogger;
import org.openbravo.service.db.DalBaseProcess;

import com.basemovil.serviceproxy.ExportJob;
import com.basemovil.serviceproxy.PendingFileList;
import com.basemovil.serviceproxy.ServiceException;
import com.basemovil.serviceproxy.ServiceProxy;

public class DownloadSalesOrders extends DalBaseProcess {

  private ProcessLogger logger;
  private ConnectionProvider connection;

  public void doExecute(ProcessBundle bundle) throws Exception {

    logger = bundle.getLogger();
    connection = bundle.getConnection();

    String orgId = bundle.getContext().getOrganization();
    Organization org = OBDal.getInstance().get(Organization.class, orgId);
    final OBCriteria<BMSALESConfiguration> obCriteria = OBDal.getInstance().createCriteria(
        BMSALESConfiguration.class);
    if (!orgId.equals("0")) {
      obCriteria.setFilterOnReadableOrganization(false);
      obCriteria.add(Expression.eq(BMSALESConfiguration.PROPERTY_ORGANIZATION, org));
    }

    for (final BMSALESConfiguration conf : obCriteria.list()) {

      ServiceProxy proxy = new ServiceProxy(conf.getCompanyAlias(), conf.getAdminUser(), conf
          .getAdminPassword(), conf.getServerUrl());
      logger.log("BMSALESConfiguration: " + conf.getIdentifier() + "\n");

      final PendingFileList list = proxy.getPendingFileList();
      if (list.getError() != 0) {
        throw new ServiceException(list.getMessage(), list.getError());
      }

      for (final ExportJob job : list.getJobs()) {

        logger.log("Processing Pending Job: " + job.getId() + "/n");
        File file = downloadFile(proxy, job.getId());
        FileHeader header = readHeader(file);
        readLines(header, file, bundle, conf.getOrganization());
        // proxy.markFile(job.getId());

      }
    }
  }

  // Receives a zip file and extracts it
  public File downloadFile(final ServiceProxy proxy, final Long jobId) throws ServiceException {
    final String fileName = jobId + ".zip";
    final File file = new File(fileName);
    FileOutputStream out = null;
    InputStream zis = null;
    ZipFile zip = null;
    File target = null;
    try {
      out = new FileOutputStream(file);
      proxy.getFile(jobId, out);
      out.close();
      zip = new ZipFile(file);
      final ZipEntry entry = zip.entries().nextElement();
      final String ext = entry.getName().substring(entry.getName().lastIndexOf('.'));
      String targetName = file.getName();
      if (!targetName.endsWith(ext)) {
        targetName = targetName.substring(0, targetName.lastIndexOf('.')) + ext;
      }
      target = new File(file.getParentFile(), targetName);
      logger.log("Temporary file saved: " + target.getAbsolutePath() + "/n");
      zis = zip.getInputStream(entry);
      out = new FileOutputStream(target);
      int c = zis.read();
      while (c != -1) {
        out.write(c);
        c = zis.read();
      }
      out.close();
      zis.close();
      zip.close();
      file.delete();

    } catch (IOException e) {
      throw new ServiceException(e);
    } finally {
      if (out != null)
        try {
          out.flush();
        } catch (Exception e) {
        }
      if (out != null)
        try {
          out.close();
        } catch (Exception e) {
        }
      if (zis != null)
        try {
          zis.close();
        } catch (Exception e) {
        }
      if (zip != null)
        try {
          zip.close();
        } catch (Exception e) {
        }
    }
    return target;
  }

  private FileHeader readHeader(final File file) throws IOException {
    FileReader fr = null;
    BufferedReader in = null;
    try {
      fr = new FileReader(file);
      in = new BufferedReader(fr);
      return new FileHeader(in.readLine());
    } finally {
      if (in != null)
        try {
          in.close();
        } catch (Exception e) {
        }
      if (fr != null)
        try {
          fr.close();
        } catch (Exception e) {
        }
    }
  }

  private void readLines(final FileHeader header, File file, ProcessBundle bundle, Organization org)
      throws Exception {
    FileInputStream fis = null;
    LineNumberReader in = null;
    ArrayList<String> orderList = new ArrayList<String>();
    try {
      fis = new FileInputStream(file);
      in = new LineNumberReader(new InputStreamReader(fis, header.getEncoding()));
      String line = in.readLine();
      String objectName = null;

      while (line != null) {

        if (line.startsWith("2#")) {
          final StringTokenizer stk = new StringTokenizer(line.substring(3), Character
              .toString(header.getSeparator()));
          final String name = stk.nextToken();
          final String border = stk.nextToken();
          if (border.equals("<")) {
          } else {
            objectName = new String(name);
          }
        } else if (line.startsWith("3#")) {
          final StringTokenizer stk = new StringTokenizer(line.substring(3), Character
              .toString(header.getSeparator()));
          final String type = stk.nextToken();
          if (type.equals("N")) {

            if (objectName.equals("lineasdocumentos")) {
              insertOrderLine(line, header, bundle, org);
            } else if (objectName.equals("documentos")) {
              String uuid = insertOrder(line, header, bundle, org);
              orderList.add(uuid);
            }
          }
        }
        line = in.readLine();
      }
    } catch (IOException e) {
      throw new ServiceException(e);
    } finally {
      if (in != null)
        try {
          in.close();
        } catch (Exception e) {
        }
      if (fis != null)
        try {
          fis.close();
        } catch (Exception e) {
        }
    }
    file.delete();
    completeOrders(orderList, bundle);

  }

  public String insertOrder(String line, FileHeader header, ProcessBundle bundle, Organization org) {

    final Order order = OBProvider.getInstance().get(Order.class);
    final SimpleDateFormat sdf = new SimpleDateFormat();
    sdf.applyPattern("yyyyMMddHHmmss");

    final TextTokenizer stk = new TextTokenizer(line.substring(3), Character.toString(header
        .getSeparator()));

    stk.next(); // type
    stk.next(); // errorCode
    String custom_id = stk.next(); // Custom_id

    DocumentType docType = OBDal.getInstance().get(DocumentType.class, stk.next()); // Order_type_id

    final OBCriteria<BusinessPartner> obcBP = OBDal.getInstance().createCriteria(
        BusinessPartner.class);
    obcBP.add(Expression.eq(BusinessPartner.PROPERTY_SEARCHKEY, stk.next())); // customer_id
    BusinessPartner bp = obcBP.list().get(0);

    stk.next(); // confirmed_date
    stk.next(); // sent_date
    stk.next(); // delivered_date
    stk.next(); // status
    stk.next(); // transmitedd_date

    User user = OBDal.getInstance().get(User.class, stk.next()); // user_id
    String date = stk.next(); // created_date

    stk.next(); // exclude_vat
    String comments = stk.next(); // comments
    stk.next(); // discount
    stk.next(); // sell_route_id
    stk.next(); // deliver_route_id
    stk.next(); // per_discount
    stk.next(); // longitude
    stk.next(); // latitude
    stk.next(); // geo_timestamp
    try {
      order.setOrderDate(sdf.parse(date));
      order.setAccountingDate(sdf.parse(date));
      String delDate = stk.next(); // delivery_date
      order.setScheduledDeliveryDate(sdf.parse(((delDate != null && !delDate.equals("")) ? delDate
          : date)));
    } catch (Exception e) {
    }
    stk.next(); // payment_type_id

    order.setOrderReference(custom_id);
    order.setClient(OBDal.getInstance().get(Client.class, bundle.getContext().getClient()));
    order.setOrganization(org);
    order.setCreationDate(new Date());
    order.setCreatedBy(user);
    order.setUpdated(new Date());
    order.setUpdatedBy(user);
    order.setActive(true);
    order.setDocumentNo(Utility.getDocumentNo(bundle.getConnection(), bundle.getContext().toVars(),
        "", "C_Order", docType.getId(), docType.getId(), false, true));
    order.setDocumentStatus("DR");
    order.setDocumentAction("CO");
    order.setDocumentType(OBDal.getInstance().get(DocumentType.class, "0"));
    order.setTransactionDocument(docType);
    order.setBusinessPartner(bp);
    order.setPartnerAddress(bp.getBusinessPartnerLocationList().get(0));
    if (bp.getBusinessPartnerLocationList().get(0).isInvoiceToAddress())
      order.setInvoiceAddress(bp.getBusinessPartnerLocationList().get(0));
    if (bp.getBusinessPartnerLocationList().get(0).isShipToAddress())
      order.setDeliveryLocation(bp.getBusinessPartnerLocationList().get(0));
    order.setCurrency(bp.getPriceList().getCurrency());
    order.setFormOfPayment(bp.getFormOfPayment());
    order.setPaymentTerms(bp.getPaymentTerms());
    order.setInvoiceTerms(bp.getInvoiceTerms());
    order.setDeliveryTerms(bp.getDeliveryTerms());
    order.setFreightCostRule("I");
    order.setDeliveryMethod(bp.getDeliveryMethod());
    order.setPriority("5");
    order.setGrandTotalAmount(new BigDecimal("0"));
    order.setSummedLineAmount(new BigDecimal("0"));
    order.setFreightAmount(new BigDecimal("0"));
    order
        .setWarehouse(OBDal.getInstance().get(Warehouse.class, bundle.getContext().getWarehouse()));
    order.setPriceList(bp.getPriceList());
    order.setDescription("Document: " + custom_id + " imported from bmSalesOb.\n" + comments);
    order.setSalesRepresentative(user);
    order.setSalesTransaction(true);
    order.setUserContact(bp.getADUserList().get(0));

    OBDal.getInstance().save(order);
    OBDal.getInstance().flush();

    logger.log("Order inserted:" + order.getId() + "\n");
    return order.getId();

  }

  public void insertOrderLine(String readline, FileHeader header, ProcessBundle bundle,
      Organization org) throws Exception {

    final OrderLine orderLine = OBProvider.getInstance().get(OrderLine.class);

    final SimpleDateFormat sdf = new SimpleDateFormat();
    sdf.applyPattern("yyyyMMddHHmmss");

    final TextTokenizer stk = new TextTokenizer(readline.substring(3), Character.toString(header
        .getSeparator()));

    stk.next(); // Type
    stk.next(); // errorCode
    stk.next(); // (Line) Custom_id
    String order_id = stk.next(); // order_id

    OBCriteria<Order> obc = OBDal.getInstance().createCriteria(Order.class);
    obc.add(Expression.eq(Order.PROPERTY_ORDERREFERENCE, order_id));

    final Order o = obc.list().get(0);

    final OBCriteria<Product> obcP = OBDal.getInstance().createCriteria(Product.class);
    obcP.add(Expression.eq(Product.PROPERTY_SEARCHKEY, stk.next())); // product_id
    Product p = obcP.list().get(0);

    stk.next(); // status
    String price = stk.next(); // price
    String qty = stk.next(); // quantity_requested
    stk.next(); // quantity_granted
    stk.next(); // sell_quantity_requested
    stk.next(); // sell_quantity_granted
    stk.next(); // zc_quantity_requested
    stk.next(); // zc_quantity_granted
    stk.next(); // zc_sell_quantity_requested
    stk.next(); // zc_sell_quantity_granted
    stk.next(); // money_discount
    stk.next(); // percent_discount
    stk.next(); // sell_unit_id
    stk.next(); // zc_sell_unit_id
    stk.next(); // order_type_id
    stk.next(); // custom_price
    BigDecimal priceActual = new BigDecimal(((price != null && !price.equals("")) ? price : "0"));
    String priceLi = stk.next(); // official_price
    BigDecimal priceList = new BigDecimal((priceLi != null && !priceLi.equals("")) ? priceLi
        : price);

    // C_ORDERLINE_ID set by DAL
    // AD_CLIENT_ID set by DAL
    orderLine.setOrganization(org); // AD_ORG_ID
    orderLine.setActive(true); // ISACTIVE
    orderLine.setCreationDate(new Date()); // CREATED
    orderLine.setCreatedBy(o.getCreatedBy()); // CREATEDBY
    orderLine.setUpdated(new Date()); // UPDATED
    orderLine.setUpdatedBy(o.getUpdatedBy()); // UPDATEDBY

    List<OrderLine> oll = o.getOrderLineList();
    oll.add(orderLine);
    o.setOrderLineList(oll); // C_ORDER_ID

    orderLine.setSalesOrder(o);

    OBCriteria<OrderLine> obcLine = OBDal.getInstance().createCriteria(OrderLine.class);
    obcLine.add(Expression.eq(OrderLine.PROPERTY_SALESORDER, o));
    obcLine.addOrderBy(OrderLine.PROPERTY_LINENO, false);

    if (obcLine.list().isEmpty()) {
      orderLine.setLineNo(new Long(10));
    } else {
      orderLine.setLineNo(obcLine.list().get(0).getLineNo());
      orderLine.setLineNo(orderLine.getLineNo() + new Long(10));
    }

    orderLine.setOrderDate(o.getOrderDate());// DATEORDERED
    orderLine.setScheduledDeliveryDate(o.getScheduledDeliveryDate());// DATEPROMISED
    // DATEDELIVERED
    // DATEINVOICED
    // DESCRIPTION
    orderLine.setProduct(p);// M_PRODUCT_ID
    orderLine.setWarehouse(o.getWarehouse());// M_WAREHOUSE_ID
    // DIRECTSHIP
    orderLine.setUOM(p.getUOM());// C_UOM_ID
    orderLine.setOrderedQuantity(new BigDecimal(qty));// QTYORDERED
    orderLine.setReservedQuantity(new BigDecimal("0"));// QTYRESERVED
    orderLine.setDeliveredQuantity(new BigDecimal("0")); // QTYDELIVERED
    orderLine.setInvoicedQuantity(new BigDecimal("0")); // QTYINVOICED
    orderLine.setCurrency(o.getCurrency());// C_CURRENCY_ID
    orderLine.setListPrice(priceList);// PRICELIST
    orderLine.setUnitPrice(priceActual);// PRICEACTUAL
    orderLine.setPriceLimit(priceList); // PRICELIMIT
    orderLine.setLineNetAmount(new BigDecimal("0"));// LINENETAMT
    orderLine.setFreightAmount(new BigDecimal("0")); // FREIGHTAMT

    String billAddress;
    if (o.getInvoiceAddress() == null) {
      billAddress = o.getPartnerAddress().getId();
    } else
      billAddress = o.getInvoiceAddress().getId();

    String deliveryDate = Utility.formatDate(o.getScheduledDeliveryDate(), bundle.getContext()
        .getJavaDateFormat());

    String tax = Tax.get(bundle.getConnection(), p.getId(), deliveryDate, org.getId(), o
        .getWarehouse().getId(), billAddress, o.getPartnerAddress().getId(), null, true);
    orderLine.setTax(OBDal.getInstance().get(TaxRate.class, tax)); // C_TAX_ID

    // M_ATTRIBUTESETINSTANCE_ID
    // QUANTITYORDER
    // M_PRODUCT_UOM_ID

    OBDal.getInstance().save(orderLine);
    OBDal.getInstance().flush();

    logger.log("Line inserted: " + orderLine.getId() + ". In order: " + o.getId() + "\n");
  }

  public void completeOrders(ArrayList<String> orderList, ProcessBundle bundle) {

    // Get c_order_post process
    Process process = OBDal.getInstance().get(Process.class, "104");

    // For each record create a new pinstance
    for (String record : orderList) {



      OBContext.getOBContext().setInAdministratorMode(true);
      final ProcessInstance pInstance = OBProvider.getInstance().get(ProcessInstance.class);
      pInstance.setProcess(process);
      pInstance.setActive(true);
      pInstance.setRecordID(new String(record).toUpperCase());
      pInstance.setUserContact(OBDal.getInstance().get(User.class, bundle.getContext().getUser()));
      OBDal.getInstance().save(pInstance);
      OBDal.getInstance().flush();
      OBContext.getOBContext().setInAdministratorMode(false);

      try {
        final Connection connection = OBDal.getInstance().getConnection();
        final PreparedStatement ps = connection.prepareStatement("SELECT * FROM C_Order_Post(?)");
        ps.setString(1, pInstance.getId());
        ps.execute();
      } catch (Exception e) {
        System.out.println("Uich excepcion cazada");
        throw new IllegalStateException(e);
      }

      OBCriteria<Order> obc = OBDal.getInstance().createCriteria(Order.class);
      obc.add(Expression.eq(Order.PROPERTY_ID, record));
      final Order o = obc.list().get(0);
      o.setOrderReference("");
      OBDal.getInstance().save(o);
      OBDal.getInstance().flush();

      // refresh the pInstance as the SP has changed it
      OBDal.getInstance().getSession().refresh(pInstance);

      if (!(pInstance.getErrorMsg().isEmpty())) {
        logger.log(pInstance.getResult() + "\n");
        logger.log(pInstance.getErrorMsg() + "\n");
      } else {
        logger.log("Order completed: " + pInstance.getRecordID() + "\n");
        OBDal.getInstance().commitAndClose();
      }
    }
  }
}
