use serialport::SerialPort;
use std::io::Error;


/// The `Err` type is hard-wired to [`printer::Error`](struct.Error.html).
pub type Result<T> = std::result::Result<T, Error>;

//  Report Type
#[derive(Debug)]
pub enum ReportType {
    XReport,
    ZReport
}

//  Setup Type
#[derive(Debug)]
pub enum SetupType {
    Status
}

//  Firmware Version
#[derive(Debug)]
pub enum FirmwareVersion {
    V01,
    V02,
    V03,
    V04,
    V05,
    V06,
    V07,
    V08,
    V09,
    V10
}

//  Document Type
#[derive(Debug)]
pub enum DocumentType {
    Invoice, 
    CreditMemo,
    DebitMemo,
    NonFiscalDocument
}

//  Struct for Printer Setup
#[derive(Debug)]
pub struct Setup {
    pub setup_type: SetupType,
    pub setup_id: Option<i32>,
    pub firmware_version: Option<FirmwareVersion>
}

pub trait SetupAction {
    //  New Instance
    fn new(setup_type: SetupType) -> Self;
}

impl SetupAction for Setup {
    fn new(setup_type: SetupType) -> Self {
        Setup { 
            setup_type, 
            setup_id: None,
            firmware_version: None
        }
    }
}

#[derive(Debug)]
pub struct PrinterResult {
    pub id: Option<i32>, 
    pub last_invoice_no: Option<String>, 
    pub last_credit_memo_no: Option<String>,
    pub last_debit_memo_no: Option<String>, 
    pub last_non_fiscal_no: Option<String>,
    pub last_closing_no: Option<String>,
}

//  Struct for Report
#[derive(Debug)]
pub struct Report {
    pub report_type: ReportType,
    pub report_id: Option<i32>,
    pub tax_id: Option<String>,
    pub date_from: Option<String>,
    pub date_to: Option<String>,
    pub number_from: Option<i32>,
    pub number_to: Option<i32>,
}

pub trait ReportAction {
    //  New Instance
    fn new(report_type: ReportType) -> Self;
}

impl ReportAction for Report {
    fn new(report_type: ReportType) -> Self {
        Report { 
            report_type, 
            report_id: None,
            tax_id: None,
            date_from: None,
            date_to: None,
            number_from: None,
            number_to: None
        }
    }
}

//  Struct for Document
#[derive(Debug)]
pub struct Document {
    pub document_id: Option<i32>,
    pub document_type: DocumentType,
    pub document_uuid: String,
    pub reversal_document_no: Option<String>,
    pub reversal_fiscal_printer_no: Option<String>,
    pub reversal_document_date: Option<String>,
    pub document_no: Option<String>,
    pub document_date: Option<String>,
    pub description: Option<String>,
    pub document_note: Option<String>,
    pub po_reference_no: Option<String>,
    pub so_reference_no: Option<String>,
    pub sales_representative_value: Option<String>,
    pub sales_representative_name: Option<String>,
    pub sales_region_value: Option<String>,
    pub sales_region_name: Option<String>,
    pub payment_term: Option<String>,
    pub document_type_name: Option<String>,
    pub warehouse_name: Option<String>,
    pub amount_in_words: Option<String>,
    pub total_lines: Option<f32>,
    pub grand_total: Option<f32>,
    pub pos_name: Option<String>,
    pub discount_printed: Option<String>,
    pub products_quantities: Option<i32>,
    pub address_1: Option<String>,
    pub address_2: Option<String>,
    pub address_3: Option<String>,
    pub address_4: Option<String>,
    pub business_partner_name: Option<String>,
    pub business_partner_tax_id: Option<String>,
    pub duns: Option<String>,
    pub city_name: Option<String>,
    pub country_name: Option<String>,
    pub region_name: Option<String>,
    pub delivery_address: Option<String>,
    pub delivery_phone: Option<String>,
    pub document_lines: Option<Vec<DocumentLine>>,
    pub payments: Option<Vec<Payment>>,
    pub taxes: Option<Vec<Tax>>
}

#[derive(Debug)]
pub struct DocumentLine {
    pub invoice_line_id: Option<i32>,
    pub product_value: Option<String>,
    pub product_name: Option<String>,
    pub product_description: Option<String>,
    pub product_barcode: Option<String>,
    pub quantity: Option<f32>,
    pub product_price: Option<f32>,
    pub product_price_list: Option<f32>,
    pub discount: Option<f32>,
    pub tax_rate: Option<f32>
}

#[derive(Debug)]
pub struct Payment {
    pub invoice_payment_id: Option<i32>,
    pub tender_type_name: Option<String>,
    pub amount: Option<f32>,
    pub converted_amount: Option<f32>,
    pub payment_method_value: Option<String>,
    pub tender_type_value: Option<String>,
    pub payment_method_name: Option<String>,
    pub reference_no: Option<String>,
    pub payment_date: Option<String>,
    pub currency_code: Option<String>
}

#[derive(Debug)]
pub struct Tax {
    pub invoice_tax_id: Option<i32>,
    pub tax_value: Option<String>,
    pub tax_name: Option<String>,
    pub tax_amount: Option<f32>,
    pub tax_rate: Option<f32>,
    pub tax_base_amount: Option<f32>
}

//  A driver for handle printer connection
pub trait FiscalPrinterDriver {
    //  New Instance
    fn new() -> Self;
    //  Send a command
    fn send_command(&mut self, _handler: &mut Box<dyn SerialPort>, _command: Vec<u8>) -> Result<bool>;
    //  Read Port
    fn read(&mut self, _handler: &mut Box<dyn SerialPort>) -> Result<Vec<u8>>;
}

pub trait FiscalPrinter {
    //  New Instance
    fn new() -> Self;
    //  Setup
    fn setup(&mut self, _handler: &mut Box<dyn SerialPort>, _setup: Setup) -> Result<PrinterResult>;
    //  Print Report
    fn print_report(&mut self, _handler: &mut Box<dyn SerialPort>, _report: Report) -> Result<PrinterResult>;
    //  Print Report
    fn print_document(&mut self, _handler: &mut Box<dyn SerialPort>, _document: Document) -> Result<PrinterResult>;
}