//    uniCenta oPOS  - Touch Friendly Point Of Sale
//    Copyright (c) 2009-2018 uniCenta & previous Openbravo POS works
//    https://unicenta.com
//
//    This file is part of uniCenta oPOS
//
//    uniCenta oPOS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//   uniCenta oPOS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with uniCenta oPOS.  If not, see <http://www.gnu.org/licenses/>.

package com.unicenta.pos.printer.printer;

import com.unicenta.data.gui.JMessageDialog;
import com.unicenta.data.gui.MessageInf;
import com.unicenta.pos.forms.AppLocal;
import com.unicenta.pos.printer.DevicePrinter;
import com.unicenta.pos.printer.ticket.BasicTicket;
import com.unicenta.pos.printer.ticket.BasicTicketForPrinter;
import com.unicenta.pos.util.ReportUtils;
import com.unicenta.pos.util.SelectPrinter;
import lombok.extern.slf4j.Slf4j;

import javax.print.*;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OrientationRequested;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.HashMap;

/**
 * Class DevicePrinterPrinter is responsible for printing tickets using system <br>
 * printers. It takes into consideration if a user set a printer as a receipt <br>
 * printer or not.
 * <p>For receipt printers lenght of a receipt must be calculated in this class.</p>
 * <p>For normal printers number of pages must be calculated dynamically in the <br>
 * class PrintableTicket @see com.unicenta.pos.printer.printer.PrintableTicket
 *
 * @author jaroslawwozniak
 */
@Slf4j
public class DevicePrinterPrinter implements DevicePrinter {

  private Component parent;
  /*name of a printer*/
  private String m_sName;
  /*a ticket to print*/
//    private BasicTicketForPrinter m_ticketcurrent;
  private BasicTicket m_ticketcurrent;
  /*system printer*/
  private PrintService printservice;

//    // For Page Size 72mm x 200mm && MediaSizeName A4.
//    private static final int imageable_width = 190;
//    private static final int imageable_height = 546;
//    private static final int imageable_x = 10;
//    private static final int imageable_y = 287;
//    private static final Media media = MediaSizeName.ISO_A4;

//    // For Page Size A4 && MediaSizeName A4.
//    private static final int imageable_width = 451;
//    private static final int imageable_height = 698;
//    private static final int imageable_x = 72;
//    private static final int imageable_y = 72;
//    private static final Media media = MediaSizeName.ISO_A4;

  private int imageable_width;
  private int imageable_height;
  private int imageable_x;
  private int imageable_y;
  private Media media;

  // JG 16 May 12 use multicatch
  private static final HashMap<String, MediaSizeName> mediasizenamemap = new HashMap<>();

  /**
   * Creates a new instance of DevicePrinterPrinter
   *
   * @param parent
   * @param printername      - name of printer that will be called in the system
   * @param imageable_x
   * @param imageable_y
   * @param imageable_height
   * @param imageable_width
   * @param mediasizename
   */
  public DevicePrinterPrinter(Component parent, String printername, int imageable_x, int imageable_y, int imageable_width, int imageable_height, String mediasizename) {

    this.parent = parent;
    m_sName = "Printer"; // "AppLocal.getIntString("printer.screen");
    m_ticketcurrent = null;
    printservice = ReportUtils.getPrintService(printername);

    this.imageable_x = imageable_x;
    this.imageable_y = imageable_y;
    this.imageable_width = imageable_width;
    this.imageable_height = imageable_height;
    this.media = getMedia(mediasizename);
  }

  /**
   * Getter that returns the name of a printer
   *
   * @return m_sName a name of a printer
   */
  @Override
  public String getPrinterName() {
    return m_sName;
  }

  /**
   * Getter that returns the description of a printer
   *
   * @return description of a printer
   */
  @Override
  public String getPrinterDescription() {
    return null;
  }

  /**
   * Getter that returns the printer's component
   *
   * @return printer's component
   */
  @Override
  public JComponent getPrinterComponent() {
    return null;
  }

  /**
   * Method that sets the current ticket as a null
   */
  @Override
  public void reset() {
    m_ticketcurrent = null;
  }

  /**
   * Method that is responsible for start a new ticket
   */
  @Override
  public void beginReceipt() {
    m_ticketcurrent = new BasicTicketForPrinter();
  }

  /**
   * Method that is responsible for printing an image
   *
   * @param image a buffered image object
   */
  @Override
  public void printImage(BufferedImage image) {
    m_ticketcurrent.printImage(image);
  }

  @Override
  public void printLogo() {
  }

  /**
   * @param type
   * @param position
   * @param code
   */
  @Override
  public void printBarCode(String type, String position, String code) {
    m_ticketcurrent.printBarCode(type, position, code);
  }

  /**
   * Method that is responsible for starting a new line on a receipt
   *
   * @param iTextSize a size of text in the line
   */
  @Override
  public void beginLine(int iTextSize) {
    m_ticketcurrent.beginLine(iTextSize);
  }

  /**
   * Method that is responsible for printing text
   *
   * @param iStyle style of text
   * @param sText  text to print
   */
  @Override
  public void printText(int iStyle, String sText) {
    m_ticketcurrent.printText(iStyle, sText);
  }

  /**
   * Method that is responsible for ending a line
   */
  @Override
  public void endLine() {
    m_ticketcurrent.endLine();
  }

  /**
   * Method that is responsible for ending and printing a ticket<br>
   * It manages to get a printerJob, set the name of the job, get a Book object<br>
   * and print the receipt
   */
  @Override
  public void endReceipt() {

    try {

      PrintService ps;

      if (printservice == null) {
        String[] printers = ReportUtils.getPrintNames();
        if (printers.length == 0) {
          log.error(AppLocal.getIntString("message.noprinters"));
          ps = null;
        } else {
          SelectPrinter selectprinter = SelectPrinter.getSelectPrinter(parent, printers);
          selectprinter.setVisible(true);
          if (selectprinter.isOK()) {
            ps = ReportUtils.getPrintService(selectprinter.getPrintService());
          } else {
            ps = null;
          }
        }
      } else {
        ps = printservice;
      }

      if (ps != null) {

        PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
        aset.add(OrientationRequested.PORTRAIT);
        aset.add(new JobName(AppLocal.APP_NAME + " - Document", null));
        aset.add(media);

        DocPrintJob printjob = ps.createPrintJob();
        Doc doc = new SimpleDoc(new PrintableBasicTicket(m_ticketcurrent, imageable_x, imageable_y, imageable_width, imageable_height), DocFlavor.SERVICE_FORMATTED.PRINTABLE, null);

        printjob.print(doc, aset);
      }

    } catch (PrintException ex) {
      log.error(ex.getMessage());
      JMessageDialog.showMessage(parent, new MessageInf(MessageInf.SGN_WARNING, AppLocal.getIntString("message.printererror"), ex));
    }

    //ticket is not needed any more
    m_ticketcurrent = null;
  }

  /**
   * Method that is responsible for opening a drawer
   */

  @Override
  public void openDrawer() {
    // Una simulacion
    Toolkit.getDefaultToolkit().beep();
  }

  private static MediaSizeName getMedia(String mediasizename) {
    return mediasizenamemap.get(mediasizename);
  }

  static {
    mediasizenamemap.put("Postcard", MediaSizeName.JAPANESE_POSTCARD);
    mediasizenamemap.put("Statement", MediaSizeName.INVOICE);

    mediasizenamemap.put("Letter", MediaSizeName.NA_LETTER);
    mediasizenamemap.put("Executive", MediaSizeName.EXECUTIVE);
    mediasizenamemap.put("Legal", MediaSizeName.NA_LEGAL);

    mediasizenamemap.put("A0", MediaSizeName.ISO_A0);
    mediasizenamemap.put("A1", MediaSizeName.ISO_A1);
    mediasizenamemap.put("A2", MediaSizeName.ISO_A2);
    mediasizenamemap.put("A3", MediaSizeName.ISO_A3);
    mediasizenamemap.put("A4", MediaSizeName.ISO_A4);
    mediasizenamemap.put("A5", MediaSizeName.ISO_A5);
    mediasizenamemap.put("A6", MediaSizeName.ISO_A6);
    mediasizenamemap.put("A7", MediaSizeName.ISO_A7);
    mediasizenamemap.put("A8", MediaSizeName.ISO_A8);
    mediasizenamemap.put("A9", MediaSizeName.ISO_A9);
    mediasizenamemap.put("A10", MediaSizeName.ISO_A10);

    mediasizenamemap.put("B0", MediaSizeName.JIS_B0);
    mediasizenamemap.put("B1", MediaSizeName.JIS_B1);
    mediasizenamemap.put("B2", MediaSizeName.JIS_B2);
    mediasizenamemap.put("B3", MediaSizeName.JIS_B3);
    mediasizenamemap.put("B4", MediaSizeName.JIS_B4);
    mediasizenamemap.put("B5", MediaSizeName.JIS_B5);
    mediasizenamemap.put("B6", MediaSizeName.JIS_B6);
    mediasizenamemap.put("B7", MediaSizeName.JIS_B7);
    mediasizenamemap.put("B8", MediaSizeName.JIS_B8);
    mediasizenamemap.put("B9", MediaSizeName.JIS_B9);
    mediasizenamemap.put("B10", MediaSizeName.JIS_B10);

    mediasizenamemap.put("ISOB0", MediaSizeName.ISO_B0);
    mediasizenamemap.put("ISOB1", MediaSizeName.ISO_B1);
    mediasizenamemap.put("ISOB2", MediaSizeName.ISO_B2);
    mediasizenamemap.put("ISOB3", MediaSizeName.ISO_B3);
    mediasizenamemap.put("ISOB4", MediaSizeName.ISO_B4);
    mediasizenamemap.put("ISOB5", MediaSizeName.ISO_B5);
    mediasizenamemap.put("ISOB6", MediaSizeName.ISO_B6);
    mediasizenamemap.put("ISOB7", MediaSizeName.ISO_B7);
    mediasizenamemap.put("ISOB8", MediaSizeName.ISO_B8);
    mediasizenamemap.put("ISOB9", MediaSizeName.ISO_B9);
    mediasizenamemap.put("ISOB10", MediaSizeName.ISO_B10);
    mediasizenamemap.put("EnvISOB0", MediaSizeName.ISO_B0);
    mediasizenamemap.put("EnvISOB1", MediaSizeName.ISO_B1);
    mediasizenamemap.put("EnvISOB2", MediaSizeName.ISO_B2);
    mediasizenamemap.put("EnvISOB3", MediaSizeName.ISO_B3);
    mediasizenamemap.put("EnvISOB4", MediaSizeName.ISO_B4);
    mediasizenamemap.put("EnvISOB5", MediaSizeName.ISO_B5);
    mediasizenamemap.put("EnvISOB6", MediaSizeName.ISO_B6);
    mediasizenamemap.put("EnvISOB7", MediaSizeName.ISO_B7);
    mediasizenamemap.put("EnvISOB8", MediaSizeName.ISO_B8);
    mediasizenamemap.put("EnvISOB9", MediaSizeName.ISO_B9);
    mediasizenamemap.put("EnvISOB10", MediaSizeName.ISO_B10);

    mediasizenamemap.put("C0", MediaSizeName.ISO_C0);
    mediasizenamemap.put("C1", MediaSizeName.ISO_C1);
    mediasizenamemap.put("C2", MediaSizeName.ISO_C2);
    mediasizenamemap.put("C3", MediaSizeName.ISO_C3);
    mediasizenamemap.put("C4", MediaSizeName.ISO_C4);
    mediasizenamemap.put("C5", MediaSizeName.ISO_C5);
    mediasizenamemap.put("C6", MediaSizeName.ISO_C6);

    mediasizenamemap.put("EnvPersonal", MediaSizeName.PERSONAL_ENVELOPE);
    mediasizenamemap.put("EnvMonarch", MediaSizeName.MONARCH_ENVELOPE);
    mediasizenamemap.put("Monarch", MediaSizeName.MONARCH_ENVELOPE);
    mediasizenamemap.put("Env9", MediaSizeName.NA_NUMBER_9_ENVELOPE);
    mediasizenamemap.put("Env10", MediaSizeName.NA_NUMBER_10_ENVELOPE);
    mediasizenamemap.put("Env11", MediaSizeName.NA_NUMBER_11_ENVELOPE);
    mediasizenamemap.put("Env12", MediaSizeName.NA_NUMBER_12_ENVELOPE);
    mediasizenamemap.put("Env14", MediaSizeName.NA_NUMBER_14_ENVELOPE);
    mediasizenamemap.put("c8x10", MediaSizeName.NA_8X10);

    mediasizenamemap.put("EnvDL", MediaSizeName.ISO_DESIGNATED_LONG);
    mediasizenamemap.put("DL", MediaSizeName.ISO_DESIGNATED_LONG);
    mediasizenamemap.put("EnvC0", MediaSizeName.ISO_C0);
    mediasizenamemap.put("EnvC1", MediaSizeName.ISO_C1);
    mediasizenamemap.put("EnvC2", MediaSizeName.ISO_C2);
    mediasizenamemap.put("EnvC3", MediaSizeName.ISO_C3);
    mediasizenamemap.put("EnvC4", MediaSizeName.ISO_C4);
    mediasizenamemap.put("EnvC5", MediaSizeName.ISO_C5);
    mediasizenamemap.put("EnvC6", MediaSizeName.ISO_C6);
  }
}