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

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

#include "serialterm.h"
#include "serialmon.h"
#include "terminal.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("SerialTerm",str)

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

LibraryItem* SerialTerm::libraryItem()
{
    return new LibraryItem(
        tr("Serial Terminal"),
        "Peripherals",
        "serialterm.png",
        "SerialTerm",
        SerialTerm::construct ); 
}

SerialTerm::SerialTerm( QString type, QString id )
          : Component( type, id )
          , UsartModule( nullptr, id+"-Uart" )
          , eElement( (id+"-eElement") )
{
    m_graphical = true; 

    m_area = QRect(-16,-16, 72, 32 ); 
    setLabelPos(-20,-32 ); 

    m_pin.resize(2); 

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

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

    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 ); 
    QObject::connect( m_button, &CustomButton::clicked, [=](){ onbuttonclicked();  }); 

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

    m_terminal = new Terminal( MainWindow::self() ); 
    m_terminal->setWindowTitle( id ); 
    QObject::connect( m_terminal, &Terminal::sendBytes, [=](QByteArray d){ sendByteArray( d );  } ); 
    QObject::connect( m_terminal, &Terminal::closed   , [=](){ TerminalClosed();  } ); 

    setBaudRate( 9600 ); 

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

    addPropGroup( { tr("Main"), {
//new BoolProp  <Chip>( "Logic_Symbol","","", this, &Chip::logicSymbol, &Chip::setLogicSymbol ),
    }, 0 } ); 

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

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

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

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

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

void SerialTerm::updateStep()
{
    if( m_uartData.size() ){
        if( !m_sending ){
            m_sending = true; 
            Simulator::self()->addEvent( 1, this ); 
        }
    } else m_sending = false; 

    update(); 
}

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

void SerialTerm::sendByteArray( QByteArray data )
{
    m_uartData += data; 
}

void SerialTerm::sendByte( uint8_t data )
{
    m_uartData += data; 
}

void SerialTerm::byteReceived( uint8_t byte )
{
    m_receiver->getData(); 
    printIn( byte ); 
    m_terminal->received( byte ); 
    m_receiving = true; 
}

void SerialTerm::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 ); 
        UsartModule::sendByte( byte ); 
    }
    else m_sending = false; 
}

void SerialTerm::onbuttonclicked()
{
    if( m_button->isChecked() ){
        m_terminal->show();  //slotOpenTerm(); 
        m_button->setText( tr("Close") ); 
    }else{
        m_terminal->hide();  //m_monitor->close(); 
        m_button->setText( tr("Open") ); 
    }
}

void SerialTerm::setIdLabel( QString id )
{
    Component::setIdLabel( id ); 
    setMonitorTittle( id ); 
    m_terminal->setWindowTitle( id ); 
}

void SerialTerm::slotOpenTerm()
{
    UsartModule::openMonitor( idLabel(), 0, /*send=*/false ); 
    //m_monitor->activateSend(); 
}

void SerialTerm::setSerialMon( bool s )
{
    if( s ) m_terminal->show();  //slotOpenTerm(); 
}

void SerialTerm::monitorClosed()
{
    UsartModule::monitorClosed(); 
    //m_button->setChecked( false ); 
}

void SerialTerm::TerminalClosed()
{
    m_button->setChecked( false ); 
    m_button->setText( tr("Open") ); 
}

void SerialTerm::setflip()
{
    Component::setflip(); 
    m_proxy->setPos( QPoint( 8 + ( 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 SerialTerm::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 SerialTerm::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_pin[0]->connector() )
    {
        if( m_sending ) p->setBrush( Qt::yellow ); 
        else            p->setBrush( Qt::red ); 
    }
    else p->setBrush( Qt::black ); 
    p->drawRoundedRect( -5,-11, 8, 6, 2, 2 );  // Tx led

    if( m_pin[1]->connector() )
    {
        if( m_receiving ){
            m_receiving = false; 
            p->setBrush( Qt::yellow ); 
        }else p->setBrush( Qt::red ); 
    }
    else p->setBrush( Qt::black ); 
    p->drawRoundedRect(-5, 5, 8, 6, 2, 2 );  // Rx led

    Component::paintSelected( p ); 
}
