/***************************************************************************
 *   Copyright (C) 2003-2005 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "binarycounter.h"

#include "logic.h"
#include "libraryitem.h"

#include <kiconloader.h>
#include <klocale.h>

Item* BinaryCounter::construct( ItemDocument *itemDocument, bool newItem, const char *id )
{
	return new BinaryCounter( (ICNDocument*)itemDocument, newItem, id );
}

LibraryItem* BinaryCounter::libraryItem()
{
	QStringList ids;
	ids << "ec/binary_counter" << "ec/4_bit_binary_counter";
	return new LibraryItem(
		ids,
		i18n("Binary Counter"),
		i18n("Integrated Circuits"),
		"ic1.png",
		LibraryItem::lit_component,
		BinaryCounter::construct
			);
}

BinaryCounter::BinaryCounter( ICNDocument *icnDocument, bool newItem, const char *id )
	: Component( icnDocument, newItem, id ? id : "binary_counter" )
{
	m_name = i18n("Binary Counter");
	m_desc = i18n("Holds an internal count, which changes when the clock input <i>&gt;</i> pin is pulsed.<br><br>"
				  "Normal operation: <i>en</i> (Enable) and <i>u/d</i> (Up/Down) are held high, <i>r</i> (Reset) is low.");

	enLogic = inLogic = rLogic = udLogic = 0L;
	
	b_reset = false;
	b_triggerHigh = true;
	b_oldIn = false;
	m_value = 0;
	b_en = false;
	b_ud = false;
	m_numBits = 0;
	m_maxValue = false;
	m_bDoneLogicIn = false;
	
	createProperty( "trig", Variant::Type::Select );
	property("trig")->setCaption( i18n("Trigger Edge") );
	property("trig")->setAllowed( QStringList::split( ',', "Rising,Falling" ) );
	property("trig")->setValue("Rising");
	
	createProperty( "bitcount", Variant::Type::Int );
	property("bitcount")->setCaption( i18n("Bit Count") );
	property("bitcount")->setMinValue(1);
	property("bitcount")->setMaxValue(26);
	property("bitcount")->setValue(4);
}


BinaryCounter::~BinaryCounter()
{
}


void BinaryCounter::dataChanged()
{
	initPins( dataInt("bitcount") );
	
	b_triggerHigh = dataString("trig") == "Rising";
	setDisplayText( ">", b_triggerHigh ? "^>" : "_>" );
}


void BinaryCounter::initPins( unsigned numBits )
{
	if ( m_numBits == numBits )
		return;
	
	QStringList pins;
	pins << "en" << ">" << "u/d" << "r";
	
	for ( int i = 0; i < QABS(4-int(numBits)); i++ )
		pins << "";
	
	for ( int i = numBits-1; i >= 0; i-- )
		pins << QChar('A'+i);
	
	initDIPSymbol( pins, 64 );
	initDIP(pins);
	
	if ( m_numBits < numBits )
	{
		for ( unsigned i = m_numBits; i < numBits; i++ )
			m_pLogicOut[i] = createLogicOut( ecNodeWithID( QChar('A'+i) ), false );
	}
	else
	{
		for ( unsigned i = numBits; i < m_numBits; i++ )
		{
			QString id = QChar('A'+i);
			removeElement( m_pLogicOut[i], false );
			removeDisplayText(id);
			removeNode(id);
		}
	}
	
	m_numBits = numBits;
	m_maxValue = (1<<m_numBits)-1;
	
	if (!m_bDoneLogicIn)
	{
		enLogic = createLogicIn( ecNodeWithID("en") );
		inLogic = createLogicIn( ecNodeWithID(">") );
		rLogic = createLogicIn( ecNodeWithID("r") );
		udLogic = createLogicIn( ecNodeWithID("u/d") );
	
		enLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::enStateChanged) );
		inLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::inStateChanged) );
		rLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::rStateChanged) );
		udLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::udStateChanged) );
		
		m_bDoneLogicIn = true;
	}
	
	outputValue();
}


void BinaryCounter::inStateChanged( bool state )
{
	if ( (state != b_oldIn) && b_en && !b_reset && state == b_triggerHigh )
	{
		m_value += (b_ud) ? 1 : -1;
		
		if ( m_value < 0 )
			m_value = m_maxValue;
		
		else if ( m_value > m_maxValue )
			m_value = 0;
		
		outputValue();
	}
	
	b_oldIn = state;
}


void BinaryCounter::rStateChanged( bool state )
{
	b_reset = state;
	if (b_reset)
	{
		m_value = 0;
		outputValue();
	}
}


void BinaryCounter::enStateChanged( bool state )
{
	b_en = state;
}


void BinaryCounter::udStateChanged( bool state )
{
	b_ud = state;
}


void BinaryCounter::outputValue()
{
	for ( unsigned i = 0; i < m_numBits; i++ )
		m_pLogicOut[i]->setHigh( m_value & (1 << i) );
}

