/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.shtrih.jpos.fiscalprinter.receipt;

/**
 *
 * @author V.Kravtsov
 */
import com.shtrih.fiscalprinter.receipt.PrinterReceipt;
import org.apache.log4j.*;

import jpos.*;
import com.shtrih.util.*;
import com.shtrih.fiscalprinter.*;
import com.shtrih.jpos.fiscalprinter.*;
import com.shtrih.fiscalprinter.receipt.*;
import com.shtrih.fiscalprinter.command.*;

public class SalesReceipt extends CustomReceipt implements FiscalReceipt {

    private int receiptType = 0;
    private boolean isOpened = false;
    private boolean hasItems = false;
    private static Logger logger = Logger.getLogger(SalesReceipt.class);

    public SalesReceipt(
            ReceiptPrinter printer,
            PrinterData printerData,
            FiscalDay fiscalDay,
            PrinterReceipt receipt) {
        super(printer, printerData, fiscalDay, receipt);
    }

    public boolean isOpened() {
        return isOpened;
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            // Restore the interrupted status
            logger.error("InterruptedException", e);
            Thread.currentThread().interrupt();
        }
    }

    public void openReceipt(int receiptType)
            throws Exception {
        if (!isOpened) {
            printer.openReceipt(receiptType);
            this.receiptType = receiptType;
            isOpened = true;
        }
    }

    public void beginFiscalReceipt(
            boolean printHeader)
            throws Exception {
        receiptType = 0;
        isOpened = false;
        hasItems = false;
        receipt.reset();
    }

    public void printRecItem(String description, long price, int quantity,
            int vatInfo, long unitPrice, String unitName)
            throws Exception {

        openReceipt(PrinterConst.SMFP_RECTYPE_SALE);
        printer.printPreLine();
        // if unitPrice is zero then we use price and quantity = 1000
        if (unitPrice == 0) {
            quantity = 1000;
        } else {
            if (quantity == 0) {
                quantity = 1000;
            }
            price = unitPrice;
        }
        description = printer.printDescription(description);

        // receipt was just opened
        if (!hasItems) {
            printSale(price, quantity, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printSale(price, quantity, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printStorno(price, quantity, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
        printer.printPostLine();
    }

    public void endFiscalReceipt(boolean printHeader)
            throws Exception {
        PrinterStatus status = printer.getPrinter().waitForPrinting();
        if (status.getPrinterMode().isReceiptOpened()) {
            if (receipt.isCancelled()) {
                printer.getPrinter().cancelReceipt();
                fiscalDay.cancelFiscalRec();
            } else {
                long[] sum = receipt.getPaymentSums();
                printer.getPrinter().closeReceipt(sum[0], sum[1], sum[2], sum[3],
                        0, 0, 0, 0, 0, printerData.closeReceiptText);
                fiscalDay.closeFiscalRec();
                // Print may not respond for some time
                sleep(printerData.recCloseSleepTime);
            }
        } else {
            if (receipt.isCancelled()) {
                printer.printText(
                        PrinterConst.SMFP_STATION_REC,
                        printerData.TEXT_REC_CANCEL, 
                        printer.getPrinter().getParams().fontNumber);
            }
        }
        printer.waitForPrinting();
    }

    public void printRecRefund(String description, long amount, int vatInfo)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_RETSALE);
        printer.printPreLine();
        description = printer.printDescription(description);

        // receipt was just opened
        if (!hasItems) {
            printSaleRefund(amount, 1000, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printStorno(amount, 1000, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printSaleRefund(amount, 1000, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
    }

    public void printRecTotal(
            long total,
            long payment,
            long payType,
            String description)
            throws Exception {
        receipt.addPayment(payment, payType);
        printer.printPreLine();
        printer.printPostLine();
    }

    public void printRecItemVoid(String description, long price,
            int quantity, int vatInfo, long unitPrice, String unitName)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_RETSALE);
        printer.printPreLine();
        // if unitPrice is zero - use price and quantity = 1000
        if (unitPrice == 0) {
            quantity = 1000;
        } else {
            if (quantity == 0) {
                quantity = 1000;
            }
            price = unitPrice;
        }
        description = printer.printDescription(description);
        if (!hasItems) {
            printSaleRefund(price, quantity, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printStorno(price, quantity, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printSaleRefund(price, quantity, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
        printer.printPostLine();
    }

    public void printRecSubtotal(long amount)
            throws Exception {
        printer.printPreLine();
        printer.printStrings(printerData.subtotalText, "="
                + StringUtils.amountToString(printer.getSubtotal()));
        printer.printPostLine();
    }

    public void printRecItemAdjustment(int adjustmentType,
            String description, long amount, int vatInfo)
            throws Exception {
        printer.printPreLine();
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:
                description = printer.printDescription(description);
                printDiscount(amount, vatInfo, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:
                description = printer.printDescription(description);
                printCharge(amount, vatInfo, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_DISCOUNT:
                description = printer.printDescription(description);
                amount = receipt.getItemPercentAdjustmentAmount(amount);
                printDiscount(amount, vatInfo, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_SURCHARGE:
                description = printer.printDescription(description);
                amount = receipt.getItemPercentAdjustmentAmount(amount);
                printCharge(amount, vatInfo, 0, 0, 0, description);
                break;

            default: {
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue) + "adjustmentType");
            }
        }
    }

    public void printRecSubtotalAdjustment(int adjustmentType,
            String description, long amount)
            throws Exception {

        checkAdjustment(adjustmentType, amount);
        printer.printPreLine();
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:
                description = printer.printDescription(description);
                printDiscount(amount, 0, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:
                description = printer.printDescription(description);
                printCharge(amount, 0, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_DISCOUNT:
                description = printer.printDescription(description);
                amount = printer.getSubtotal() * amount / 10000;
                printDiscount(amount, 0, 0, 0, 0, description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_SURCHARGE:
                description = printer.printDescription(description);
                amount = printer.getSubtotal() * amount / 10000;
                printCharge(amount, 0, 0, 0, 0, description);
                break;

            default: {
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue)
                        + "AdjustmentType");
            }
        }
        printer.printPostLine();
    }

    public void printRecVoidItem(String description, long amount,
            int quantity, int adjustmentType, long adjustment, int vatInfo)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_RETSALE);
        description = printer.printDescription(description);
        if (!hasItems) {
            printSaleRefund(amount, quantity, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printStorno(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printSaleRefund(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
    }

    public void printRecRefundVoid(String description, long amount,
            int vatInfo)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_SALE);
        description = printer.printDescription(description);

        if (!hasItems) {
            printSale(amount, 1000, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printStorno(amount, 1000, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printSale(amount, 1000, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
    }

    public void printRecPackageAdjustment(int adjustmentType,
            String description, String vatAdjustment)
            throws Exception {

        PackageAdjustments adjustments = new PackageAdjustments();
        adjustments.parse(vatAdjustment);
        checkAdjustments(adjustmentType, adjustments);
        PackageAdjustment adjustment;
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:

                adjustments.parse(vatAdjustment);
                checkAdjustments(FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT, adjustments);
                printer.printText(PrinterConst.SMFP_STATION_REC, description, 
                        printer.getPrinter().getParams().fontNumber);
                for (int i = 0; i < adjustments.size(); i++) {
                    adjustment = adjustments.getItem(i);
                    printDiscount(adjustment.amount, adjustment.vat,
                            0, 0, 0, "");
                }
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:

                adjustments.parse(vatAdjustment);
                checkAdjustments(FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE, adjustments);

                printer.printText(description);
                for (int i = 0; i < adjustments.size(); i++) {
                    adjustment = adjustments.getItem(i);
                    printCharge(adjustment.amount,
                            adjustment.vat, 0, 0, 0, "");
                }
                break;

            default:
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue)
                        + "adjustmentType");
        }
    }

    public void printRecPackageAdjustVoid(int adjustmentType,
            String vatAdjustment)
            throws Exception {
        PackageAdjustments adjustments = new PackageAdjustments();
        adjustments.parse(vatAdjustment);
        checkAdjustments(adjustmentType, adjustments);
        PackageAdjustment adjustment;

        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:

                adjustments.parse(vatAdjustment);
                checkAdjustments(FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT,
                        adjustments);

                printer.printText("Void discount");
                for (int i = 0; i < adjustments.size(); i++) {
                    adjustment = adjustments.getItem(i);
                    printCharge(adjustment.amount, adjustment.vat, 0, 0, 0, "");
                }
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:

                adjustments.parse(vatAdjustment);
                checkAdjustments(FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE,
                        adjustments);

                printer.printText("Void charge");
                for (int i = 0; i < adjustments.size(); i++) {
                    adjustment = adjustments.getItem(i);
                    printDiscount(adjustment.amount, adjustment.vat, 0, 0, 0, "");
                }
                break;

            default:
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue)
                        + "adjustmentType");
        }
    }

    public void printRecSubtotalAdjustVoid(int adjustmentType, long amount)
            throws Exception {
        printer.printPreLine();
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:
                printDiscount(amount, 0, 0, 0, 0, "");
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:
                printCharge(amount, 0, 0, 0, 0, "");
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_DISCOUNT:
                amount = printer.getSubtotal() * amount / 10000;
                printCharge(amount, 0, 0, 0, 0, "");
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_SURCHARGE:
                amount = printer.getSubtotal() * amount / 10000;
                printDiscount(amount, 0, 0, 0, 0, "");
                break;

            default: {
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue)
                        + "adjustmentType");
            }
        }
    }

    public void printRecItemAdjustmentVoid(int adjustmentType,
            String description, long amount, int vatInfo)
            throws Exception {
        checkAdjustment(adjustmentType, amount);
        printer.printPreLine();
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:
                description = printer.printDescription(description);
                printCharge(amount, vatInfo, 0, 0, 0,
                        description);
                break;

            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:
                description = printer.printDescription(description);
                printDiscount(amount, vatInfo, 0, 0, 0,
                        description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_DISCOUNT:
                description = printer.printDescription(description);
                amount = receipt.getItemPercentAdjustmentAmount(amount);
                printCharge(amount, vatInfo, 0, 0, 0,
                        description);
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_SURCHARGE:
                description = printer.printDescription(description);
                amount = receipt.getItemPercentAdjustmentAmount(amount);
                printDiscount(amount, vatInfo, 0, 0, 0,
                        description);
                break;

            default: {
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue)
                        + "adjustmentType");
            }
        }
    }

    public void printRecItemRefund(
            String description,
            long amount,
            int quantity,
            int vatInfo,
            long unitAmount,
            String unitName)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_RETSALE);
        printer.printPreLine();
        // if unitPrice is zero then we use price and quantity = 1000
        if (unitAmount == 0) {
            quantity = 1000;
        } else {
            if (quantity == 0) {
                quantity = 1000;
            }
            amount = unitAmount;
        }
        description = printer.printDescription(description);

        // receipt was just opened
        if (!hasItems) {
            printSaleRefund(amount, quantity, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_SALE:
                    printStorno(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printSaleRefund(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
        printer.printPostLine();
    }

    public void printRecItemRefundVoid(String description,
            long amount,
            int quantity,
            int vatInfo,
            long unitAmount,
            String unitName)
            throws Exception {
        openReceipt(PrinterConst.SMFP_RECTYPE_SALE);
        printer.printPreLine();
        // if unitPrice is zero - use price and quantity = 1000
        if (unitAmount == 0) {
            quantity = 1000;
        } else {
            if (quantity == 0) {
                quantity = 1000;
            }
            amount = unitAmount;
        }
        description = printer.printDescription(description);
        if (!hasItems) {
            printSale(amount, quantity, printerData.department,
                    vatInfo, description);
            hasItems = true;
        } else {
            switch (receiptType) {
                case PrinterConst.SMFP_RECTYPE_RETSALE:
                    printStorno(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;

                case PrinterConst.SMFP_RECTYPE_SALE:
                    printSale(amount, quantity, printerData.department,
                            vatInfo, description);
                    break;
            }
        }
        printer.printPostLine();
    }

    public void printRecVoid(String description)
            throws Exception {
        printer.printText(description);
        receipt.cancel();
    }

    public boolean isPayed() {
        return receipt.isPayed();
    }

    public PrinterReceipt getReceipt() {
        return receipt;
    }

    public void printSale(long price, long quantity, int department,
            int vatInfo, String description)
            throws Exception {
        printer.getPrinter().printSale(price, quantity, department,
                vatInfo, 0, 0, 0, description);
        receipt.printSale(price, quantity, department,
                vatInfo, description);
    }

    public void printSaleRefund(long price, long quantity, int department,
            int vatInfo, String description)
            throws Exception {
        printer.getPrinter().printVoidSale(price, quantity, department,
                vatInfo, 0, 0, 0, description);

        receipt.printSaleRefund(price, quantity, department,
                vatInfo, description);
    }

    public void printStorno(long price, int quantity,
            int department, int vatInfo, String description)
            throws Exception {
        printer.getPrinter().printVoidItem(price, quantity, department,
                vatInfo, 0, 0, 0, description);
        receipt.printStorno(price, quantity, department,
                vatInfo, description);
    }

    public void printDiscount(long amount, int tax1, int tax2, int tax3,
            int tax4, String text)
            throws Exception {
        printer.getPrinter().printDiscount(amount, tax1, tax2, tax3, tax4, text);
        receipt.printDiscount(amount, tax1, tax2, tax3, tax4, text);
    }

    public void printCharge(long amount, int tax1, int tax2, int tax3,
            int tax4, String text)
            throws Exception {
        printer.getPrinter().printCharge(amount, tax1, tax2, tax3, tax4, text);
        receipt.printCharge(amount, tax1, tax2, tax3, tax4, text);
    }

    private void checkPercents(long amount)
            throws Exception {
        if ((amount < 0) || (amount > 10000)) {
            throw new JposException(JposConst.JPOS_E_EXTENDED,
                    FiscalPrinterConst.JPOS_EFPTR_BAD_ITEM_AMOUNT);
        }
    }

    public void checkAdjustment(int adjustmentType, long amount)
            throws Exception {
        switch (adjustmentType) {
            case FiscalPrinterConst.FPTR_AT_AMOUNT_DISCOUNT:
            case FiscalPrinterConst.FPTR_AT_AMOUNT_SURCHARGE:
                break;

            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_DISCOUNT:
            case FiscalPrinterConst.FPTR_AT_PERCENTAGE_SURCHARGE:
                checkPercents(amount);
                break;

            default: {
                throw new JposException(JposConst.JPOS_E_ILLEGAL,
                        Localizer.getString(Localizer.InvalidParameterValue) + "adjustmentType");
            }
        }
    }

    private void checkAdjustments(int adjustmentType,
            PackageAdjustments adjustments)
            throws Exception {
        PackageAdjustment adjustment;
        for (int i = 0; i < adjustments.size(); i++) {
            adjustment = adjustments.getItem(i);
            checkAdjustment(adjustmentType, adjustment.amount);
        }
    }
}
