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

#include <QPainter>

#include "max72xx_matrix.h"
#include "itemlibrary.h"
#include "ledbase.h"
#include "connector.h"
#include "simulator.h"
#include "iopin.h"

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

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

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

LibraryItem* Max72xx_matrix::libraryItem()
{
    return new LibraryItem(
        tr("Max72xx matrix"),
        "Leds",
        "max72xx.png",
        "Max72xx_matrix",
        Max72xx_matrix::construct);
}

Max72xx_matrix::Max72xx_matrix( QString type, QString id )
              : LogicComponent( type, id )
{
    m_graphical = true;
    m_numDisplays = 4;
    m_area = QRectF(-36, -44, 4+64*m_numDisplays+4, 88 );

    m_colorList << "Yellow"<<"Red"<<"Green"<<"Blue"<<"Orange"<<"Purple"<<"White";

    m_colors[0] = QColor( 255-25, 255-25,   0    ); // Yellow
    m_colors[1] = QColor( 255-25, 140-25, 100-25 ); // Red
    m_colors[2] = QColor(   0   , 255-25, 100-25 ); // Green
    m_colors[3] = QColor( 150-25, 150-25, 255-25 ); // Blue
    m_colors[4] = QColor( 255-25, 200-25,  25    ); // Orange
    m_colors[5] = QColor( 255-25,  75-25, 240-25 ); // Purple
    m_colors[6] = QColor( 255-25, 255-25, 255-25 ); // White
    m_ledColor = 0;

    m_pinCS  = new Pin( 270, QPoint(-12, 52), id+"PinCS", 0, this );
    m_pinDin = new Pin( 270, QPoint(-20, 52), id+"PinDin", 0, this );
    m_pinSck = new IoPin( 270, QPoint(-28, 52), id+"PinSck", 0, this, input );

    m_pinCS->setLabelText(  " CS" );
    m_pinDin->setLabelText( " DIN" );
    m_pinSck->setLabelText( " SCK" );

    m_pin.resize( 3 );
    m_pin[0] = m_pinCS;
    m_pin[1] = m_pinDin;
    m_pin[2] = m_pinSck;

    setColorStr("Yellow");

    eClockedDevice::setClockPin( m_pinSck );

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

    setLabelPos(-32, -58, 0);
    setShowId( true );

    Max72xx_matrix::initialize();

    addPropGroup( { tr("Main"), {
        new StrProp<Max72xx_matrix>("Color", tr("Color"), LedBase::getColorList()
                                   , this, &Max72xx_matrix::colorStr,    &Max72xx_matrix::setColorStr,0,"enum" ),
        new IntProp<Max72xx_matrix>("NumDisplays", tr("Size"),""
                                   , this, &Max72xx_matrix::numDisplays, &Max72xx_matrix::setNumDisplays,0,"uint" ),
    }, groupNoCopy } );
}
Max72xx_matrix::~Max72xx_matrix(){}

void Max72xx_matrix::stamp()
{
    m_pinSck->changeCallBack( this ); // Register for Sck changes callback
    m_pinCS->changeCallBack( this );  // Register for CS changes callback
}

void Max72xx_matrix::initialize()
{
    for( int i=0; i<16; i++)
    {
        for( int j=0; j<8; j++ ) m_ram[i][j] = 0;
        m_intensity[i] = 0;
    }
    m_decodemode = 0;
    m_scanlimit = 0;
    m_shutdown = true;
    m_test = false;

    m_rxReg = 0;
    m_inBit = 0;
    m_inDisplay = 0;

    updateStep();
}

void Max72xx_matrix::voltChanged()
{
    updateClock();

    if( m_pinCS->getVoltage()>1.6 )            // CS high: not selected
    {
        m_rxReg = 0;
        m_inBit  = 0;
        m_inDisplay = 0;
        return;
    }
    if( m_clkState != Clock_Rising ) return;

    m_rxReg &= ~1;
    if( m_pinDin->getVoltage()>1.6 ) m_rxReg |= 1;

    if( m_inBit == 15 )
    {
        proccessCommand();
        m_inBit = 0;
        m_inDisplay++;
    }else{
        m_rxReg <<= 1;
        m_inBit++;
}   }

void Max72xx_matrix::updateStep() { update(); }

void Max72xx_matrix::proccessCommand()
{
    if ( m_inDisplay >= 16 ) return;

    int addr = (m_rxReg>>8) & 0x0F;
    switch( addr )
    {
        case 0: break; // No-Op
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:  // Digits 0 to 7
            m_ram[m_inDisplay][addr-1] = m_rxReg & 0xFF;
            break;
        case 9:  // Decode mode (only no-decode 0 mode supported)
            m_decodemode = m_rxReg & 0xFF;
            break;
        case 10: // Intensity (0-15)
            m_intensity[m_inDisplay] = 1+(m_rxReg & 0x0F);
            break;
        case 11: // Scan limit (0-7)
            m_scanlimit = m_rxReg & 0x07;
            break;
        case 12: // Shutdown
            m_shutdown = !(m_rxReg & 0x01);
            break;
        case 15: // Display test
            m_test = m_rxReg & 0x01;
            break;
}   }

void Max72xx_matrix::setNumDisplays( int displays )
{
    if( displays == m_numDisplays ) return;
    if( displays < 1 ) displays = 1;
    if( displays > 16 ) displays = 16;
    m_numDisplays = displays;

    hide();
    m_area = QRectF(-36,-44, 4+64*m_numDisplays+4, 88 );
    show();
}

void Max72xx_matrix::setColorStr( QString color )
{
    m_ledColor = m_colorList.indexOf( color );
    if( m_showVal && (m_showProperty == "Color") )
        setValLabelText( color );
}

void Max72xx_matrix::setHidden( bool hid, bool hidArea, bool hidLabel )
{
    Component::setHidden( hid, hidArea, hidLabel );
    if( hid ) m_area = QRectF(-32,-40, 64*m_numDisplays,     64 );
    else      m_area = QRectF(-36,-44, 4+64*m_numDisplays+4, 88 );
}

void Max72xx_matrix::paint( QPainter* p, const QStyleOptionGraphicsItem* o, QWidget* w )
{
    Component::paint( p, o, w );
    p->setRenderHint( QPainter::Antialiasing );

    QPen pen( Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin );
    p->setPen( pen );

    p->setBrush( QColor(50, 70, 100) );
    p->drawRoundedRect( m_area, 2, 2 );

    for( int display=0; display<m_numDisplays; display++ )
    {
        pen.setColor( QColor(Qt::black) );
        p->setPen( pen );
        p->setBrush( QColor(Qt::black) );
        p->drawRoundedRect( 64*display-32, -40, 64, 64, 2, 2 );

        int factor = m_intensity[display];
        QColor color = m_colors[m_ledColor];
        color = QColor( 25+color.red()*factor/16, 25+color.green()*factor/16, 25+color.blue()*factor/16);

        for( int row=0; row<8; row++ )
        {
            int x = 64*display-32+1;
            int y = -40+row*8+1;
            for( int col=0; col<8; col++)
            {
                int bit = m_ram[display][row] & (0x80>>col);
                if( !m_shutdown && (m_test || (m_decodemode == 0 && row <= m_scanlimit && bit)) )
                {
                    p->setBrush( color );
                    pen.setColor( color );
                }else{
                    p->setBrush( QColor( 25, 25, 25) );
                    pen.setColor( QColor(25, 25, 25) );
                }
                p->setPen( pen );
                p->drawEllipse( x, y, 6, 6 );
                x += 8;
    }   }   }

    Component::paintSelected( p );
}
