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

#include <QDomDocument>
#include <QFileInfo>
#include <QTranslator>
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>

#include "mcu.h"
#include "cpu8bits.h"
#include "mcuport.h"
#include "mcupin.h"
#include "mcuwdt.h"
#include "mcucreator.h"
#include "scriptcpu.h"
#include "circuit.h"
#include "simulator.h"
#include "itemlibrary.h"
#include "circuitwidget.h"
#include "infowidget.h"
#include "mainwindow.h"
#include "componentlist.h"
#include "mcumonitor.h"
#include "memdata.h"
#include "mcuuart.h"
#include "mcuintosc.h"
#include "utils.h"
#include "watcher.h"

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

#include "propdialog.h"

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

Mcu* Mcu::m_pSelf = nullptr; 

LibraryItem* Mcu::libraryItem()
{
    return new LibraryItem(
        "NEW_MCU",
        "",
        "ic2.png",
        "MCU",
        Mcu::construct ); 
}

Component* Mcu::construct( QString type, QString id )
{
    m_error = 0; 
    QString device = Chip::getDevice( id ); 
    Mcu* mcu = new Mcu( type, id, device ); 
    if( !m_error) m_error = McuCreator::createMcu( mcu, id ); 

    if( m_error > 0 )
    {
        Circuit::self()->removeComp( mcu ); 
        mcu = nullptr; 
        m_pSelf = nullptr; 
        m_error = 0; 
    }
    return mcu; 
}

Mcu::Mcu( QString type, QString id, QString device )
   : Chip( type, id, device )
   , m_eMcu( this, id )
{
    qDebug() << "        Initializing"<<id; 

    m_resetPin   = nullptr; 
    m_portRstPin = nullptr; 
    m_mcuMonitor = nullptr; 
    m_scriptCpu = nullptr; 

    m_savePGM  = false; 
    m_autoLoad = false; 
    m_scripted = false; 
    m_resetPol = false; 
    m_isLinker = true; 
    m_forceFreq = true; 
    //this->m_graphical = true; 

    m_uiFreq = 0; 
    m_serialMon = -1; 

    addPropGroup( { tr("Main"), {},0} ); 

    m_device = m_name; 
    if( m_device.contains("@") ) m_device = m_device.split("@").last();  // MCU in Subcircuit

    if( m_device.startsWith("p") ) // PICs TODELETE
    {
        if( m_device.endsWith("a") ) m_device.remove( m_device.size()-1, 1 ); 
        m_device.replace("f", "F"); 
    }

    setName( m_device ); 

     // Search in Circuit folder
    QDir mcuDir = QFileInfo( Circuit::self()->getFilePath() ).absoluteDir(); 
    QString baseFile = mcuDir.absoluteFilePath( m_device+"/"+m_device ); 
    QString mcuFile = baseFile+".mcu"; 
    if( !QFile::exists( mcuFile ) )
    {
        baseFile = mcuDir.absoluteFilePath( "data/"+m_device+"/"+m_device ); 
        mcuFile = baseFile+".mcu"; 
    }

    if( !QFile::exists( mcuFile ) ) // Not found in Circuit folder, check if Defined in xml file
    {
        baseFile = ""; 
        QString xmlFile = ComponentList::self()->getDataFile( m_device ); 

        if( QFile::exists( xmlFile ) )
        {
            QDomDocument domDoc = fileToDomDoc( xmlFile, "Mcu::Mcu" ); 
            if( domDoc.isNull() ) { m_error = 1;  return;  }

            QDomElement root  = domDoc.documentElement(); 
            QDomNode    rNode = root.firstChild(); 

            bool found = false; 
            while( !rNode.isNull() )
            {
                QDomElement itemSet = rNode.toElement(); 
                QDomNode    node    = itemSet.firstChild(); 

                QString folder = ""; 
                if( itemSet.hasAttribute("folder") ) folder = itemSet.attribute("folder")+"/"; 

                while( !node.isNull() )
                {
                    QDomElement element = node.toElement(); 
                    if( element.attribute("name") == m_device )
                    {
                        QFileInfo fi( xmlFile ); 

                        QString pkg = m_device+"/"+m_device; 
                        QString dat = pkg; 

                        if( element.hasAttribute("package") ) pkg = element.attribute("package"); 
                        if( element.hasAttribute("data")    ) dat = element.attribute("data"); 

                        baseFile = fi.absolutePath()+"/"+ folder + pkg; 
                        mcuFile  = fi.absolutePath()+"/"+ folder + dat+".mcu"; 

                        found = true; 
                    }
                    if( found ) break; 
                    node = node.nextSibling(); 
                }
                if( found ) break; 
                rNode = rNode.nextSibling(); 
            }
        }
    }
    if( !QFile::exists( mcuFile ) ){
        QDir mcuDir; 
        QString folder = ComponentList::self()->getFileDir( m_device ); 

        if( !folder.isEmpty() ) // Found in folder (no xml file)
        {
            mcuDir = QDir( folder ); 
        }
        else              // Try to find a "data" folder in Circuit folder
        {
            mcuDir = QFileInfo( Circuit::self()->getFilePath() ).absoluteDir(); 
            folder = "data/"+m_device; 
        }
        mcuFile = mcuDir.absoluteFilePath( folder+"/"+m_device+".mcu" ); 
        baseFile = mcuDir.absoluteFilePath( folder+"/"+m_device ); 
    }

    if( !baseFile.isEmpty() )
    {
        m_dataFile = mcuFile;  //+".mcu"; 
        if( !QFileInfo::exists( m_dataFile ) )
        {
            qDebug() <<"Mcu::Mcu Files not found for:"<< m_device; 
            qDebug() << m_dataFile; 
            m_error = 1; 
            return; 
        }

        QString pkgeFile = baseFile+".package"; 
        if( QFileInfo::exists( pkgeFile ) ){
            QString pkgStr = fileToString( pkgeFile, "Mcu::Mcu" ); 
            m_packageList["1- "+m_device+"_DIP"] = convertPackage( pkgStr ); 
        }
        QString pkgFileLS = baseFile+"_LS.package"; 
        if( QFileInfo::exists( pkgFileLS ) ){
            QString pkgStr = fileToString( pkgFileLS, "Mcu::Mcu" ); 
            m_packageList["2- "+m_device+"_LS"] = convertPackage( pkgStr ); 
        }
    }

    if( m_packageList.isEmpty() ){
        qDebug() << "Mcu::Mcu: No Packages found for"<<m_device<<Qt::endl; 
        m_error = 1; 
        return; 
    }

    QSettings* settings = MainWindow::self()->settings(); 
    m_lastFirmDir = settings->value("lastFirmDir").toString(); 

    QDir dir( m_lastFirmDir ); 
    if( !dir.exists() ) m_lastFirmDir = Circuit::self()->getFilePath(); 

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

    qDebug() << "       "<<id<< "Initialized"<<Qt::endl; 
}
Mcu::~Mcu()
{
    if( m_mcuMonitor ) delete m_mcuMonitor; 
    if( m_pSelf == this ) m_pSelf = nullptr; 
    InfoWidget::self()->updtMcu(); 
}

void Mcu::setupMcu()
{
    m_eMcu.setup(); 

    if( m_pSelf == nullptr ) slotmain(); 

    // Main Property Group --------------------------------------

    if( m_packageList.size() > 1 )
    addProperty(tr("Main"), new StrProp<Mcu>("Package", tr("Package"), m_packageList.keys().join(",")
                                            , this, &Mcu::package, &Mcu::setPackage, propNoCopy,"enum" ) ); 

    if( m_eMcu.m_intOsc )
    {
    addProperty(tr("Main"),new DoubProp<Mcu>("Frequency", tr("Frequency"),"MHz"
                                            , this, &Mcu::uiFreq, &Mcu::setUiFreq )); 
    addProperty(tr("Main"),new BoolProp<Mcu>("ForceFreq", tr("Force this frequency"),""
                                            , this, &Mcu::forceFreq, &Mcu::setForceFreq )); 
    }

    if( m_eMcu.flashSize() )
    {
    addProperty(tr("Main"),new BoolProp<Mcu>("savePGM", tr("PGM persistent"),""
                                            , this, &Mcu::savePGM, &Mcu::setSavePGM )); 

    addProperty(tr("Main"),new StrProp <Mcu>("Program", tr("Firmware"),""
                                            , this, &Mcu::program, &Mcu::setProgram )); 

    addProperty(tr("Main"),new BoolProp<Mcu>("Auto_Load", tr("Reload hex at Simulation Start"),""
                                            , this, &Mcu::autoLoad, &Mcu::setAutoLoad )); 
    }
    if( m_eMcu.romSize() )
    addProperty(tr("Main"),new BoolProp<Mcu>("saveEepr", tr("EEPROM persistent"),""
                                            , this, &Mcu::saveEepr, &Mcu::setSaveEepr )); 

    // Config Property Group ------------------------------------

    propGroup cg = { tr("Config"), {
        new ComProperty( "", tr("Changes applied after Simulation Restart"),"","",0),}, groupNoCopy}; 

    if( m_portRstPin )
        cg.propList.append(new BoolProp<Mcu>("Rst_enabled", tr("Enable Reset Pin"),""
                                            , this, &Mcu::rstPinEnabled, &Mcu::enableRstPin ) ); 

    if( m_eMcu.m_intOsc && m_eMcu.m_intOsc->hasClockPins() )
        cg.propList.append(new BoolProp<Mcu>("Ext_Osc", tr("External Oscillator"),""
                                            , this, &Mcu::extOscEnabled, &Mcu::enableExtOsc ) ); 

    if( m_eMcu.m_wdt )
        cg.propList.append(new BoolProp<Mcu>("Wdt_enabled", tr("Enable WatchDog"),""
                                            , this, &Mcu::wdtEnabled, &Mcu::enableWdt ) ); 

    if( m_eMcu.m_intOsc && m_eMcu.m_intOsc->clkOutPin() )
        cg.propList.append(new BoolProp<Mcu>("Clk_Out", tr("Clock Out"),""
                                            , this, &Mcu::clockOut, &Mcu::setClockOut ) ); 

    if( cg.propList.size() > 1 ) addPropGroup( cg ); 


    // Hidden Property Group -----------------------------------

    propGroup hi = {"Hidden", {}, groupHidden };  // Set before Main

    hi.propList.append(new StrProp<Mcu>("varList" ,"","", this, &Mcu::varList,   &Mcu::setVarList) ); 
    hi.propList.append(new StrProp<Mcu>("cpuRegs" ,"","", this, &Mcu::cpuRegs,   &Mcu::setCpuRegs) ); 
    hi.propList.append(new StrProp<Mcu>("Links"   ,"","", this, &Mcu::getLinks , &Mcu::setLinks ) ); 
    hi.propList.append(new BoolProp<Mcu>("MainMcu","","", this, &Mcu::mainMcu , &Mcu::setMainMcu ) ); 

    if( m_eMcu.flashSize() )
    hi.propList.append(new StrProp<Mcu>("pgm"   ,"","", this, &Mcu::getPGM, &Mcu::setPGM ) ); 

    if( m_eMcu.romSize() )
    hi.propList.append(new StrProp<Mcu>("eeprom"   ,"","", this, &Mcu::getEeprom, &Mcu::setEeprom ) ); 

    if( m_eMcu.m_transModules.size() )
    hi.propList.append(new IntProp<Mcu>("SerialMon","","", this, &Mcu::serialMon, &Mcu::setSerialMon ) ); 

    if( hi.propList.size() > 0 ) addPropGroup( hi ); 

    //int index = m_isTQFP ? 2 : 0; 
    setPackage( m_packageList.keys().at( 0 ) ); 

    m_eMcu.getRamTable()->setRegisters( m_eMcu.m_regInfo.keys() ); 
    setUiFreq( m_uiFreq ); 
}

bool Mcu::propNotFound( QString prop, QString val )
{
    if( prop =="program" ) setProgram( val );  //  Old: TODELETE
    else if( prop =="Mhz" ) setUiFreq( val.toDouble()*1e6 ); 
    else return Chip::propNotFound( prop, val ); 
    return true; 
}

void Mcu::initialize()
{
    m_crashed = false; 
    if( m_mcuMonitor ) m_mcuMonitor->updateRamTable(); 
}

void Mcu::stamp()
{
    m_eMcu.reset(); 

    if( m_resetPin ){
        m_resetPin->changeCallBack( this ); 

        if( m_resetPin == m_portRstPin ) // Not dedicated Reset Pin
        {
            m_portRstPin->controlPin( true, true ); 
            m_portRstPin->setPinMode( input ); 
        }
        bool resetWarning = !m_resetPin->isConnected(); 
        m_resetPin->warning( resetWarning ); 
        if( resetWarning ) qDebug() << "    Warning!!"<<idLabel()<<"Reset pin not connected:\n"; 
    }
    else m_eMcu.start(); 

    if( m_autoLoad )
    { if( !m_eMcu.m_firmware.isEmpty() ) load( m_eMcu.m_firmware );  }
}

void Mcu::updateStep()
{
    if( m_backData ) update();   // used by ScriptDisplay

    if( m_crashed )
    {
        Simulator::self()->setWarning( /*m_warning*/0 ); 
        update(); 
    }
    if( m_mcuMonitor
     && m_mcuMonitor->isVisible() ) m_mcuMonitor->updateStep(); 

    m_eMcu.m_cpu->updateStep(); 
}

void Mcu::voltChanged() // Reset Pin callBack
{
    if( !m_resetPin ) return; 
    m_eMcu.hardReset( m_resetPin->getInpState() == m_resetPol ); 
}

void Mcu::setProgram( QString pro )
{
    if( m_savePGM ) return; 
    if( pro == "" ) return; 
    load( m_subcFolder+pro ); 
}

QString Mcu::varList()
{
    RamTable* ramTable = m_eMcu.getRamTable(); 
    if( ramTable ) return ramTable->getVarSet().join(","); 
    return ""; 
}

void Mcu::setVarList( QString vl )
{
    RamTable* ramTable = m_eMcu.getRamTable(); 
    if( !ramTable ) return; 

    if( vl.isEmpty() ) return; 
    QStringList vars = vl.split(","); 
    QStringList varSet; 
    for( QString str : vars ) // Format: {"var0/aadr0/type0", "var1/addr1/type1",...}
    {
        QStringList words = str.split("/"); 
        if( words.size() < 1 ) continue; 
        QString name = words.at(0); 
        int addr = -1; 
        QString type = "uint8"; 
        bool ok = false; 
        if( words.size() > 1 ) addr = words.at(1).toInt( &ok, 0 ); 
        if( words.size() > 2 ) type = words.at(2); 
        if( ok ) m_eMcu.getRamTable()->addVariable( name, addr, type ); 
        varSet.append( name ); 
    }
    ramTable->loadVarSet( varSet ); 
}
//{ m_eMcu.getRamTable()->loadVarSet( vl.split(",") );  }

QString Mcu::cpuRegs()
{
    Watcher* watcher = m_eMcu.cpu()->getWatcher(); 
    if( !watcher ) return ""; 
    return watcher->getVarSet().join(","); 
}

void Mcu::setCpuRegs( QString vl )
{
    Watcher* watcher = m_eMcu.cpu()->getWatcher(); 
    if( !watcher ) return; 
    m_eMcu.cpu()->getWatcher()->loadVarSet( vl.split(",") ); 
}

QString Mcu::getPGM()
{
    if( m_savePGM )
    {
        QString pgmStr; 
        QVector<int> pgm; 
        for( uint16_t val : m_eMcu.m_progMem ) pgmStr += QString::number( val )+","; 

        return pgmStr; 
    }
    return ""; 
}

void Mcu::setPGM( QString pgm )
{
    if( pgm.isEmpty() ) return; 
    QStringList valList = pgm.split(","); 
    int size = m_eMcu.flashSize(); 
    int i = 0; 
    for( QString valStr : valList )
    {
        if(! valStr.isEmpty() ) m_eMcu.setFlashValue( i, valStr.toInt() ); 
        if( ++i >= size ) break; 
    }
}

void Mcu::setEeprom( QString eep )
{
    if( eep.isEmpty() ) return; 
    QVector<int> eeprom; 
    QStringList list = eep.split(","); 
    for( QString val : list ) eeprom.append( val.toUInt() ); 

    if( eeprom.size() > 0 ) m_eMcu.setEeprom( &eeprom ); 
}

QString Mcu::getEeprom()  // Used by property, stripped to last written value.
{
    QString eeprom; 
    if( m_eMcu.m_saveEepr )
    {
        int size = m_eMcu.romSize(); 
        if( size > 0 )
        {
            bool empty = true; 
            for( int i=size-1;  i>=0;  --i )
            {
                uint8_t val = m_eMcu.getRomValue( i ); 
                if( val < 0xFF ) empty = false; 
                if( empty ) continue; 
                eeprom.prepend( QString::number( val )+"," ); 
    }   }   }
    return eeprom; 
}

void Mcu::loadEEPROM()
{
   QVector<int>* eeprom = m_eMcu.eeprom(); 
   MemData::loadData( eeprom, false ); 
   m_eMcu.setEeprom( eeprom ); 
   if( m_mcuMonitor ) m_mcuMonitor->tabChanged( 1 ); 
}

void Mcu::saveEEPROM() { MemData::saveData( m_eMcu.eeprom() );  }

void Mcu::slotLoad()
{
    QDir dir( m_lastFirmDir ); 
    if( !dir.exists() ) m_lastFirmDir = Circuit::self()->getFilePath(); 

    QString fileName = QFileDialog::getOpenFileName( nullptr, tr("Load Firmware"), m_lastFirmDir,
                       tr("All files (*.*); ; Hex Files (*.hex)")); 

    if( fileName.isEmpty() ) return;  // User cancels loading

    load( fileName ); 
}

void Mcu::slotReload()
{
    if( !m_eMcu.m_firmware.isEmpty() ) load( m_eMcu.m_firmware ); 
    else QMessageBox::warning( 0, "Mcu::slotReload", tr("No File to reload ") ); 
}

bool Mcu::load( QString fileName )
{
    if( fileName.isEmpty() ) return false; 

    QDir circuitDir = QFileInfo( Circuit::self()->getFilePath() ).absoluteDir(); 
    QString fileNameAbs  = circuitDir.absoluteFilePath( fileName ); 
    QString cleanPathAbs = circuitDir.cleanPath( fileNameAbs ); 

    if( !QFileInfo::exists( cleanPathAbs ) )
    {
        qDebug() << "Error: file doesn't exist:\n"<<cleanPathAbs<<"\n"; 
        return false; 
    }
    if( Simulator::self()->simState() > SIM_STARTING )  CircuitWidget::self()->powerCircOff(); 

    int size = m_eMcu.flashSize(); 
    QVector<int> pgm( size ); 
    for( int i=0;  i<size;  ++i ) pgm[i] = m_eMcu.getFlashValue( i ); 

    if( !MemData::loadFile( &pgm, cleanPathAbs, false, m_eMcu.m_wordSize*8, &m_eMcu ) )
        return false; 

    for( int i=0;  i<size;  ++i ) m_eMcu.setFlashValue( i, pgm.at(i) ); 
    qDebug() << "Firmware successfully loaded\n"; 

    QString firmware = circuitDir.relativeFilePath( cleanPathAbs ); 
    if( m_eMcu.m_firmware != firmware ) Circuit::self()->setChanged(); 
    m_eMcu.m_firmware = firmware; 

    m_lastFirmDir = QFileInfo( cleanPathAbs ).absolutePath(); 
    if( m_propDialog ) m_propDialog->updtValues(); 

    QSettings* settings = MainWindow::self()->settings(); 
    settings->setValue( "lastFirmDir", circuitDir.relativeFilePath( fileName ) ); 

    return true; 
}

void Mcu::contextMenu( QGraphicsSceneContextMenuEvent* event, QMenu* menu )
{
    QAction* mainAction = menu->addAction( m_theme->icon(":/subc.png"),tr("Main Mcu") ); 
    QObject::connect( mainAction, &QAction::triggered, [=](){ slotmain();  } ); 

    if( m_scriptCpu && !parentItem() )
    {
        QAction* linkCompAction = menu->addAction( m_theme->icon(":/subcl.png"),tr("Link to Component") ); 
        QObject::connect( linkCompAction, &QAction::triggered, [=](){ slotLinkComp();  } ); 
    }

    if( m_eMcu.flashSize() )
    {
        QAction* loadAction = menu->addAction( m_theme->icon(":/load.svg"),tr("Load firmware") ); 
        QObject::connect( loadAction, &QAction::triggered, [=](){ slotLoad();  } ); 

        QAction* reloadAction = menu->addAction( m_theme->icon(":/reload.svg"),tr("Reload firmware") ); 
        QObject::connect( reloadAction, &QAction::triggered, [=](){ slotReload();  } ); 

        menu->addSeparator(); 
    }

    if( m_eMcu.romSize() )
    {
        QAction* loadDaAction = menu->addAction( m_theme->icon(":/open.svg"),tr("Load EEPROM data from file") ); 
        QObject::connect( loadDaAction, &QAction::triggered, [=](){ loadEEPROM();  } ); 

        QAction* saveDaAction = menu->addAction(m_theme->icon(":/save.svg"), tr("Save EEPROM data to file") ); 
        QObject::connect( saveDaAction, &QAction::triggered, [=](){ saveEEPROM();  } ); 
    }
    menu->addSeparator(); 

    QAction* openRamTab = menu->addAction( m_theme->icon(":/terminal.svg"),tr("Open Mcu Monitor.") ); 
    QObject::connect( openRamTab, &QAction::triggered, [=](){ slotOpenMcuMonitor();  } ); 

    if( m_eMcu.m_transModules.size() )
    {
        QMenu* serMonMenu = menu->addMenu( m_theme->icon(":/serialterm.png"),tr("Open Monitor.") ); 

        for( uint i=0;  i<m_eMcu.m_transModules.size();  ++i )
        {
            QAction* act = serMonMenu->addAction( m_eMcu.m_transModules.at(i)->moduleName() ); 
            QObject::connect( act, &QAction::triggered, [=](){ slotOpenTerm( i );  } ); 
        }
    }
    menu->addSeparator(); 
    Component::contextMenu( event, menu ); 
}

void Mcu::slotmain()
{
    m_pSelf = this; 
    m_eMcu.setMain(); 
    InfoWidget::self()->updtMcu(); 
    Circuit::self()->update(); 
}

void Mcu::slotLinkComp()
{
    Linker::startLinking(); 
}

void Mcu::setIdLabel( QString id )
{
    Component::setIdLabel( id ); 
    for( uint i=0;  i<m_eMcu.m_transModules.size();  ++i )
    {
        QString id = findIdLabel(); 
        m_eMcu.m_transModules.at(i)->setMonitorTittle( id ); 
    }
}

void Mcu::slotOpenMcuMonitor()
{
    if( !m_mcuMonitor ) m_mcuMonitor = new MCUMonitor( CircuitWidget::self(), &m_eMcu ); 
    m_mcuMonitor->setWindowTitle( findIdLabel() ); 
    m_mcuMonitor->show(); 
}

void Mcu::slotOpenTerm( int num )
{
    m_eMcu.m_transModules.at(num)->openMonitor( findIdLabel(), num ); 
    m_serialMon = num; 
}

int Mcu::serialMon()
{
    if( m_serialMon < 0 ) return -1; 
    if( m_eMcu.m_transModules.at( m_serialMon )->monitorOpen() ) return m_serialMon; 
    return -1; 
}

void Mcu::setSerialMon( int s ) { if( s>=0 ) slotOpenTerm( s );  }

void Mcu::setLinkedValue( double v, int i )
{
    if( m_scriptCpu ) m_scriptCpu->setLinkedVal( v, i ); 
}

void Mcu::setLinkedString( QString str, int i )
{
    if( m_scriptCpu ) m_scriptCpu->setLinkedStr( str, i ); 
}

Pin* Mcu::addPin( QString id, QString type, QString label,
                  int pos, int xpos, int ypos, int angle, int length, int space )
{
    IoPin* pin = nullptr; 
    if( type.contains("rst") )
    {
        if( !m_resetPin )
            m_resetPin = new IoPin( angle, QPoint(xpos, ypos), m_id+"-"+id, pos-1, this, input ); 
        pin = m_resetPin; 
        m_resetPol = type.startsWith("!") ; 
    }
    else pin = m_eMcu.getIoPin( id );  // I/O Port

    if( !pin ) return nullptr; 

    QColor color = Qt::black; 
    if( !m_isLS ) color = QColor( 250, 250, 200 ); 

    if( type.startsWith("nul") )
    {
        pin->setVisible( false ); 
        pin->setLabelText( "" ); 
    }else{
        pin->setVisible( true ); 
        if( type.startsWith("inv") ) pin->setInverted( true ); 
    }

    pin->setPos( QPoint( xpos, ypos ) ); 
    pin->setPinAngle( angle ); 
    pin->setLength( length ); 
    pin->setSpace( space ); 
    pin->setLabelText( label ); 
    pin->setLabelColor( color ); 
    pin->setFlag( QGraphicsItem::ItemStacksBehindParent, false ); 
    return pin; 
}

void Mcu::setUiFreq( double freq )
{
    m_uiFreq = freq; 
    if( m_forceFreq ) m_eMcu.forceFreq( freq ); 
    else if( !m_eMcu.intOsc()->freqChanged() ) m_eMcu.forceFreq( freq );  // McuIntOsc can reconfigure frequency, if not then set directly
}

void Mcu::setForceFreq( bool f )
{
    m_forceFreq = f; 
    setUiFreq( m_uiFreq ); 
}

bool Mcu::rstPinEnabled()
{
    if( !m_portRstPin ) return true; 
    return (m_resetPin == m_portRstPin); 
}

void Mcu::enableRstPin( bool en ) // Called from Property or cfg word
{
    if( !m_portRstPin ) return; 

    m_portRstPin->controlPin( en, en ); 

    if( en ){
        m_resetPin = m_portRstPin; 
        m_portRstPin->setPinMode( input ); 
    }
    else m_resetPin = nullptr; 
}

bool Mcu::extOscEnabled()
{
    if( m_eMcu.m_intOsc ) return m_eMcu.m_intOsc->extClock(); 
    return false; 
}

void Mcu::enableExtOsc( bool en )
{
    if( m_eMcu.m_intOsc ) m_eMcu.m_intOsc->enableExtOsc( en ); 
}

bool Mcu::wdtEnabled()
{
    if( m_eMcu.m_wdt ) return m_eMcu.m_wdt->enabled(); 
    return false; 
}

void Mcu::enableWdt( bool en ) { if( m_eMcu.m_wdt ) m_eMcu.m_wdt->enable( en );  }

bool Mcu::clockOut()
{
    if( m_eMcu.m_intOsc ) return m_eMcu.m_intOsc->clockOut(); 
    return false; 
}

void Mcu::setClockOut( bool clkOut ) { if( m_eMcu.m_intOsc ) m_eMcu.m_intOsc->setClockOut( clkOut );  }

void Mcu::paint( QPainter* p, const QStyleOptionGraphicsItem* option, QWidget* widget )
{
    Chip::paint( p, option, widget ); 

    if( m_pSelf == this )
    {
        QPen pen( Qt::black, 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); 
        p->setPen( pen ); 
        p->setBrush( Qt::yellow ); 
        if( m_width == m_height ) p->drawRoundedRect( 4, 4, 4, 4 , 2, 2); 
        else                      p->drawRoundedRect( m_area.width()/2-2, -1, 4, 4 , 2, 2); 
}   }

void Mcu::mousePressEvent( QGraphicsSceneMouseEvent* event )
{
    if( m_scriptCpu
     && Simulator::self()->isRunning()
     && event->button() != Qt::RightButton )
    {
        QPointF c = event->pos(); 
        m_scriptCpu->mousePress( c.x(), c.y(), event->button() ); 
        event->accept(); 
        return; 
    }
    Chip::mousePressEvent( event ); 
}

void Mcu::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
{
    if( m_scriptCpu
     && Simulator::self()->isRunning()
     && event->button() != Qt::RightButton )
    {
        QPointF c = event->pos(); 
        m_scriptCpu->mouseRelease( c.x(), c.y(), event->button() ); 
        event->accept(); 
        return; 
    }
    Chip::mouseReleaseEvent( event ); 
}

void Mcu::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
{
    if( m_scriptCpu
     && Simulator::self()->isRunning() )
    {
        QPointF c = event->pos(); 
        m_scriptCpu->mouseMoved( c.x(), c.y() ); 
        event->accept(); 
        return; 
    }
    Chip::mouseMoveEvent( event ); 
}
