/***************************************************************************
 *   Copyright (C) 2018 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#include <QGraphicsProxyWidget>
#include <QPainter>
#include <QMenu>

#include "serialport.h"
#include "serialmon.h"
#include "itemlibrary.h"
#include "simulator.h"
#include "circuit.h"
#include "usarttx.h"
#include "usartrx.h"
#include "iopin.h"
#include "utils.h"
#include "mainwindow.h"
#include "custombutton.h"

#include "stringprop.h"
#include "boolprop.h"
#include "intprop.h"

#define tr(str) simulideTr("SerialPort",str)

Component* SerialPort::construct( QString type, QString id )
{ return new SerialPort( type, id );  }

LibraryItem* SerialPort::libraryItem()
{
    return new LibraryItem(
        tr("Serial Port"),
        "Peripherals",
        "serialport.png",
        "SerialPort",
        SerialPort::construct ); 
}

SerialPort::SerialPort( QString type, QString id )
          : Component( type, id )
          , UsartModule( nullptr, id+"-Uart" )
          , eElement( (id+"-eElement") )
{
    m_area = QRect(-32,-16, 160, 32 ); 
    m_graphical = true; 
    setLabelPos(-20,-32 ); 

    m_pin.resize(2); 

    IoPin* pinTx = new IoPin( 180, QPoint(-40,-8), id+"-pin0", 0, this, output ); 
    pinTx->setLabelText( "Tx" ); 
    pinTx->setOutHighV( 5 ); 
    m_pin[0] = pinTx; 
    m_sender->setPins( {pinTx} ); 

    IoPin* pinRx = new IoPin( 180, QPoint(-40, 8), id+"-pin1", 0, this, input ); 
    pinRx->setLabelText( "Rx" ); 
    m_pin[1] = pinRx; 
    m_receiver->setPins( {pinRx} ); 

    m_serial = new QSerialPort( /*this*/ ); 
    m_receiving = false; 
    m_autoOpen  = false; 

    m_flowControl = QSerialPort::NoFlowControl; 
    setBaudRate( 9600 ); 

    m_button = new CustomButton( ); 
    m_button->setMaximumSize( 44, 20 ); 
    m_button->setGeometry(-44,-20, 44, 20 ); 
    m_button->setCheckable( true ); 
    m_button->setText( tr("Open") ); 

    QFont font = m_button->font(); 
    font.setFamily( MainWindow::self()->defaultFontName() ); 
    font.setPixelSize(12); 
    m_button->setFont( font ); 

    m_proxy = Circuit::self()->addWidget( m_button ); 
    m_proxy->setParentItem( this ); 
    m_proxy->setPos( QPoint(-8,-10) ); 

    QObject::connect( m_button, &CustomButton::clicked , [=](){ onbuttonclicked();  }); 
    QObject::connect( m_serial, &QSerialPort::readyRead, [=](){ readData();  } ); 

    Simulator::self()->addToUpdateList( this ); 

    addPropGroup( { tr("Main"), {
        new BoolProp<SerialPort>("Auto", tr("Auto Open"), ""
                                , this, &SerialPort::autoOpen, &SerialPort::setAutoOpen ),

        new StrProp <SerialPort>("Port", tr("Port Name"), ""
                                , this, &SerialPort::port, &SerialPort::setPort ),
    }, 0 } ); 

    addPropGroup( { tr("Config"), {
        new IntProp<SerialPort>("Baudrate", tr("Baudrate"),"_Bd"
                               , this, &SerialPort::baudRate, &SerialPort::setBaudRate,0,"uint" ),

        new IntProp<SerialPort>("DataBits", tr("Data Bits"),"_bits"
                               , this, &SerialPort::dataBits, &SerialPort::setDataBits,0,"uint" ),

        new IntProp<SerialPort>("StopBits", tr("Stop Bits"),"_bits"
                               , this, &SerialPort::stopBits, &SerialPort::setStopBits,0,"uint" ),
    }, 0 } ); 

    addPropGroup( {"Hidden", {
        new BoolProp<SerialPort>("SerialMon","",""
                                , this, &SerialPort::monitorOpen, &SerialPort::setSerialMon ),
    }, groupHidden} ); 
}
SerialPort::~SerialPort(){}

void SerialPort::stamp()
{
    m_serData.clear(); 
    m_uartData.clear(); 
    m_sender->enable( true ); 
    m_receiver->enable( true ); 
    m_sending = false; 
    m_receiving = false; 

    if( m_autoOpen && !m_serial->isOpen() ) m_button->click(); 
}

void SerialPort::updateStep()
{
    if( m_serData.size() )
    {
        if( m_serial->isOpen() ) m_serial->write( m_serData ); 
        m_serData.clear(); 
    }
    else m_receiving = false; 

    if( m_uartData.size() && !m_sending ) Simulator::self()->addEvent( 1, this ); 

    update(); 
}

void SerialPort::runEvent()
{
    if( m_uartData.isEmpty() ) return; 
    sendByte( m_uartData.at( 0 ) );  // Start transaction
    m_uartData = m_uartData.right( m_uartData.size()-1 ); 
    m_sending = true; 
}

void SerialPort::open()
{
    if( m_serial->isOpen() ) close(); 

    m_serial->setPortName( m_portName ); 
    m_serial->setBaudRate( m_baudRate ); 
    m_serial->setDataBits( (QSerialPort::DataBits)dataBits() ); 
    m_serial->setParity( (QSerialPort::Parity)m_parity ); 
    m_serial->setStopBits( (QSerialPort::StopBits)m_stopBits ); 
    m_serial->setFlowControl( QSerialPort::NoFlowControl/*m_flowControl*/ ); 

    if( m_serial->open( QIODevice::ReadWrite ) )
    {
        qDebug()<<"Connected to" << m_portName; 
        m_button->setText( tr("Close") ); 
    }else{
        m_button->setChecked( false ); 
        MessageBoxNB( "Error", tr("Cannot Open Port %1:\n%2.").arg(m_portName).arg(m_serial->errorString()) ); 
    }
    m_receiving = false; 
    update(); 
}

void SerialPort::close()
{
    if( m_serial->isOpen() ) m_serial->close(); 
    m_button->setText( tr("Open") ); 
    m_receiving = false; 
    m_sending = false; 
    update(); 
}

void SerialPort::readData()
{
    m_uartData += m_serial->readAll(); 
}

void SerialPort::setflip()
{
    Component::setflip(); 
    m_proxy->setPos( QPoint( -4 + ( m_Hflip>0 ? 0 : m_button->width() ), -10 + ( m_Vflip>0 ? 0 : m_button->height() ) ) ); 
    m_proxy->setTransform( QTransform::fromScale( m_Hflip, m_Vflip ) ); 
}

void SerialPort::byteReceived( uint8_t byte )
{
    m_receiver->getData(); 
    printIn( byte ); 
    m_serData.append( byte ); 
    m_receiving = true; 
}

void SerialPort::frameSent( uint8_t data )
{
    printOut( data ); 
    if( m_uartData.size() )
    {
        uint8_t byte = m_uartData.at( 0 ); 
        m_uartData = m_uartData.right( m_uartData.size()-1 ); 
        sendByte( byte ); 
    }
    else m_sending = false; 
}

void SerialPort::slotClose()
{
    close(); 
    //Circuit::self()->removeComp( this );   /// ???
}

void SerialPort::onbuttonclicked()
{
    if( m_button->isChecked() ) open(); 
    else                        close(); 
}

void SerialPort::setIdLabel( QString id )
{
    Component::setIdLabel( id ); 
    setMonitorTittle( id ); 
}

void SerialPort::slotOpenTerm()
{
    openMonitor( idLabel(), 0 ); 
}

void SerialPort::setSerialMon( bool s )
{
    if( s ) slotOpenTerm(); 
}

void SerialPort::contextMenu( QGraphicsSceneContextMenuEvent* event, QMenu* menu )
{
    QAction* openSerMon = menu->addAction( m_theme->icon(":/terminal.svg"),tr("Open Serial Monitor.") ); 
    QObject::connect( openSerMon, &QAction::triggered, [=](){ slotOpenTerm();  } ); 

    menu->addSeparator(); 
    Component::contextMenu( event, menu ); 
}

void SerialPort::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w ); 
    
    p->setBrush( Qt::darkBlue ); 
    p->drawRoundedRect( m_area, 4, 4 ); 

    if( m_serial->isOpen() )
    {
        if( m_sending ) p->setBrush( Qt::yellow ); 
        else            p->setBrush( Qt::red ); 
    }
    else p->setBrush( Qt::black ); 
    p->drawRoundedRect( -21,-11, 8, 6, 2, 2 );  // Tx led

    if( m_serial->isOpen() )
    {
        if( m_receiving ) p->setBrush( Qt::yellow ); 
        else              p->setBrush( Qt::red ); 
    }
    else p->setBrush( Qt::black ); 
    p->drawRoundedRect( -21,  5, 8, 6, 2, 2 );  // Rx led
    //p->drawEllipse( 32,-6, 12, 12); 
    
    p->setBrush( Qt::white ); 
    QPen pen = p->pen(); 
    pen.setWidth( 0 ); 
    pen.setColor( QColor( 250, 210, 150 ) ); 
    p->setPen(pen); 
    QFont font = p->font(); 
    font.setPixelSize(11); 
    p->setFont( font ); 
    p->drawText( 40, 5, m_portName ); 

    Component::paintSelected( p ); 
}
