use std::{time::{Duration, Instant}, thread, io};

use fiscalprinter::{FiscalPrinterDriver};
use serialport::SerialPort;
use fiscalprinter::Result;
use rand::Rng;

#[derive(Clone, Copy)]
pub struct Driver {
    _last_sequence: u8
}

const MINIMUM_SEQUENCE: u8 = 32;
const MAXIMUM_SEQUENCE: u8 = 127;
const ASCII_START_CHARACTER: u8 = 2;
const ASCII_END_CHARACTER: u8 = 3;

impl Driver {

    fn next_sequence(mut self) -> u8 {
        self._last_sequence = rand::thread_rng().gen_range(MINIMUM_SEQUENCE..MAXIMUM_SEQUENCE);
        self._last_sequence
    }

    //  Checksum from PnP
    fn checksum(msg: &[u8]) -> String {
        let mut crc: u16 = 0x0;
        for hex in msg.iter() {
            let hex_cloned = hex.clone();
            crc = crc + (hex_cloned as u16);
        }
        // format!("{:0>8}", crc);
        let mut checksum_as_hex_value = format!("{:X}", crc);
        checksum_as_hex_value = format!("{:0>4}", checksum_as_hex_value);
        checksum_as_hex_value
    }

    fn build_command(self, command: Vec<u8>) -> Result<Vec<u8>> {
        let start_value = [
            ASCII_START_CHARACTER, 
            self.next_sequence()
            ];
        let end_value: &[u8] = &[ASCII_END_CHARACTER];
        let start_value: &[u8] = &start_value;
        let _command: &[u8] = &command;
        let value = [start_value, _command, end_value].concat();
        //  Calculate checksum
        let checksum_value = Driver::checksum(&value);
        let checksum_value: &[u8] = &checksum_value.as_bytes();
        //  Concat value with checksum
        let value: &[u8] = &value;
        let command_to_print = [value, checksum_value].concat();
        return Ok(command_to_print);
    }
}

impl FiscalPrinterDriver for Driver {

    fn new() -> Self {
        Driver { 
            _last_sequence: MINIMUM_SEQUENCE
        }
    }

    fn send_command(&mut self, mut _handler: &mut Box<dyn SerialPort>, _command: Vec<u8>) -> Result<bool> {
        println!("send_command {:?}" , _command);
        let _complete_command = self.build_command(_command).expect("Error with command");
        let _complete_command: &[u8] = &_complete_command;
        println!("complete command {:?}", _complete_command);
        _handler.write(_complete_command).expect("error writing on port");
        let _result = self.read(_handler).expect("Error reading port");
        Ok(_result.len() > 0)
    }

    fn read(&mut self, mut _handler: &mut Box<dyn SerialPort>) -> Result<Vec<u8>> {
        //  Read port
        let time = Instant::now();
        let mut _result: Vec<u8> = vec![0; 1000];
        let mut _size = 0;
        loop {
            thread::sleep(Duration::from_millis(10));
            match _handler.read(_result.as_mut_slice()) {
                Ok(t) => {
                    _size = t;
                },
                Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
                Err(e) => eprintln!("{:?}", e),
            }
            match time.elapsed() {
                t if t.as_millis() > 100 => break,
                _ => {
                    
                }
            }
        }
        let mut _final_result: Vec<u8> = vec![0; 0];
        if _size > 0 {
            _final_result = _result[.._size].to_vec();
            println!("Result {:?}", _final_result);
        }
        Ok(_final_result)
    }
}