#ifndef PIC_HH
#define PIC_HH
/*
   -----------------------
   PIC base class  
   -----------------------
*/
#include <stdio.h>
#include <unistd.h>

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>

using namespace std ;

// used to mark a word as "non initialized"
#define	INVALID 0xFFFFFFFF


#include "hardware.hh"
#include "FragBuffer.hh"
#include "pikdb.hh"
#include "progressObserver.hh"
#include "progressListener.hh"


/// Differents flavors of Intel HEX file formats
typedef enum {
    IHX8M, 	  ///< 8 bits "swapped" format
    IHX16,		///< 16 bits format
    IHX32		///< 8 bit format with 32 bits addresses for PIC18XXXX
} ihexfmt ;

// customize line size in HEX files
#define HEXBLKSIZE 8


/**
	\brief Base class for PICs representation.
 
  	To manage a new PIC model, you have to inherit (directly or not) from this class
	and (re)implement its virtual methods. As usual, pure virtual methods
	must be implemented, and (non-pure) virtual methods may be reimplemented.
	See documentation of each method to see if the reimplementation
  	is necessary.
 
	\sa  The PicFactory class may be used to manage a collection of pic models.
*/

class pic
{
protected:
    hardware *hard_ ; 	///< abstraction of the hardware port on which the programmer is connected
protected:
    // lower and upper limits of  PIC memory zones (bounds included)
    // set to -1 if irrelevant
    // CAUTION: These adresses are WORDS adresses
    int memaddr1, 	memaddr2 ;  ///< Limit indexes  of program adresses for this pic
    int idaddr1, 	idaddr2 ;     ///< Limit indexes of id data adresses  for this pic
    int eeaddr1 , 	eeaddr2 ;   ///< Limit indexes of EEprom  adresses  for this pic
    int cfaddr1,  cfaddr2 ;     ///< Indexes of config register(s)
    int calibration1, calibration2  ; ///< Limit Indexes of calibration words
    int wr_buf_size ; ///< PIC18XXX write buffer, in words
    const char * config_desc ; ///< config words descriptor
    const char * specific_name ; ///< specific name, (pic generated from generic model)
    FragBuffer mem ;    ///<  pic memory image
    int core;  ///< bits per word (12, 14 or 16)

    int programming_option ; ///<< can be used to control "intelligent" programming algo

    int debugmode ; ///< debug level, internally used

    int clk_delay ; // additionnal delay for buggy hardware


    //  observer used by user interface for displaying operations status
    progressObserver progress_status ;

    //  service functions
    void fill_mem() ; 		///< mark the PIC memory buffer as blank
    void waitus(unsigned int t) ; ///< wait for t microseconds

    // file IO low-level function
    /**
    	Read an hex file
    \param f the stream to be decoded
    \return an error code  :  0==OK, 1==CRC mismatch,  2==corrupted file

    Caution: As 18xxx devices use byte adresses (instead of wor adresses)
     this routine must be recoded for 16 bits devices.
    */

    virtual int readihex( ifstream& f ) ; ///< IO low-level function
    bool fetch_next_block(FragBuffer::iterator& start,  int *len) ;  ///< IO low-level function
    void write_hex_block(ostream& f, int reclen, FragBuffer::iterator& data, ihexfmt format);  ///< IO low-level function
    /**
    Display a portion of a buffer (generally used to display the mem buffer)
    \param out output stream
    \param reclen The number of words to be displayed
    \param block Iterator "pointing to" the begining of the block to be displayed
    */
    // display a block of the memory buffer
    void display_block(ostream &out, int reclen,  FragBuffer::iterator& block) ;  ///< Display a pic memory buffer

    // Display a standard warning message (programming and verification of the PIC device)
    /**
    Display a standard warning message.
    This method must be used by new pic implementors
    which wish to emit diagnostics messages during PIC programmation or verification.

    Messages generated by warning() are redirected to a graphical window if the pic class is used
    in pikdev, or to cout if the pic class is used in a text only application.

    \param out Output stream.
    \param what Message text
    \param where Faulty address
    \param chip Value read from the chip during programming or verification
    (this value is normally different from the mem value when an error message is emmitted)
    Use the default value (ie: do not use this parameter) if you don't need this output.
    \param mem Expected value for chip.
      Use the default value if you don't need this output.

    \sa verify_pic(ostream& out) and verify_pic(ostream& out)
    */
    void warning(ostream& out, const char *what,  int where ,
                 int chip=-1,  int mem=-1) ; ///< Emit a warning message during PIC programming

public:
    // constructor
    pic() ;    ///< Constructor
    // Destructor
    virtual ~pic() ;   ///<   Destructor

    /**
    Connect    the pic to a hardware port

    As a hardware port is a blocking ressource, it cannot be allocated by the pic class,
    so you have to allocate and open it yourself.
    Moreover, the destructor does not close nor deallocate the port.

    \param the_port A hardware port, which do not need to be open before connexion.

    */
    // connect the pic software to an hardware port
    void setPort(hardware *the_port) ;  ///<  connect the pic to the hardware port

    /**
       set/reset debug modes. for implementor only.
    */
    void debugon(int mask) ;  ///< For implementor only
    /**
       reset debug modes. for implementor only.
    */
    void debugoff(int mask) ;  ///< For implementor only


    // configuration routine

    // hex files IO
    /**
      Loads a local hex file in the PIC memory buffer.
      This function automagically detect the type of Intel .hex format

      \param fname File name

      \return true if sucessful
    */
    //  load an Intel .hex file into PIC memory buffer
    bool load_hex( const char *fname ) ; ///< load an Intel .hex file into PIC memory buffer
    /**
      Write out  the PIC memory buffer in Intel .hex format.
      Non used blocks of the buffer are not writed.

      \param fname File name
      \param format

      \return 0 if OK, 1 if file creation error
    */
    //  Write out  the PIC memory buffer in Intel .hex format.
    int  write_hex( const char *fname , ihexfmt format) ; ///< write the PIC memory buffer

    /**
    Displays the PIC memory buffer (unused blocks are not displayed).
    \param out Output stream.
    \return The number of used/displayed blocks
    */
    //     Displays the PIC memory buffer (unused blocks are not displayed).
    int display_mem(ostream &out) ; ///< Displays the PIC memory buffer

    /**
     Interpret a command string, and sends commands to the programmer

     \param cmd The command string
     \param word Optionnal parameter, used to transmit data to/from programmer

     \return Number of errors

     About commands:
     
     Each command is coded by a single letter.
     
     When a command is used to drive signal line of the programmer board,
     UPPERCASE letter means logical high and lowercase means logical low
     (logical to physical translation is performed by the hardware object)
     
     The following commands fall into this category :     
      - c/C = clock
      - d/D = data
      - p/P = power (vdd)
      - b/B = burn signal(vpp)

      Other commands are:
    - wnnnnn = wait nnnnn microseconds (nnnnn is decimal)
    - S  = send data word
    - R = read data word
    - s = send byte
    - r = read byte
    - F = fake word read (read from pic, but don't send back via *word,
          keep the value for subsequent Verify)
    - f = dummy byte read (read from pic, but don't send back via *word,
          keep the value for subsequent verify)
    - knn = send command nn to PIC (nn is decimal)
    - v = verify byte data (compare previously readed byte  against *word & 0xFF)
    - V = verify word data (compare previously readed word  against *word)
    - ; = NOP (can be used as separator - useful in debug mode)
    - xhh = send a 8 bits constant (hh must be a 2 digits hex constant)       (PIC 18F support)
    - Xhhhh =  send a 16 bits constant (hhhh must be a 4 digits hex constant)   (PIC 18F support)
    -  , = a shorthand for w1

    \warning No blanks allowed, please use  the ";" command if you need to clarify the command string.


    Here is an example from the PIC 16F84 programming algorithm    
    \code
    // Program the PIC's main program code memory
    // 16C/F84 only need 1 programming pulse, and no additionnal pulses
    for ( x = memaddr1; x <= memaddr2 ; x++ )
    {
      // the following call to pulseEngine means :
      // send command 2, wait 1us, send word *(mem+x), wait 1us, send command 8,
      // wait 10ms, send command 4, wait 1us, read back programed word, compare to *(mem+x)
      // and return status
      n = pulseEngine("k2w1Sw1k8w10000k4w1FV", mem+x) ;
      if( n != 0 )
     {
      stat |= 1 ;   // report failure
      warning(out,"Prog memory",x) ;
      }
     pulseEngine("k6w1", 0) ;	 // inc addr (command 6, wait 1us)
    }

    \endcode

    \sa classes pic16F84 and pic12C508 for examples.
    */
    //  Interpret a command sequence string
    int pulseEngine(const char *cmd, unsigned int *word=0) ; ///< interpret a command sequence string

    /**
     Modify one word of the PIC memory buffer
    */
    // Modify one word of the PIC memory buffer
    void set_memory(unsigned int adr,unsigned int newvalue) ; ///<  modify one word of the mem buffer

    /**
     Makes an error message from the error code  returned by verify_pic() and program_pic().
     May be reimplemented
    */
    //  Convert error code from   verify_pic() or program_pic()
    const char *error_msg(int code) ; ///< Convert error code from   verify_pic() or program_pic()

    /**
       Send a word of data to the PIC
       \param d The word to be sent to the PIC. This word is masked according to the word length of the PIC.
      \note may be reimplemented
    */
    //  Send a word of data to the PIC
    virtual void send_data( unsigned int d ) ; ///< Send a word of data to the PIC

    /**
       Send a nbb bits word to the PIC

       \param d The word to be sent
       \param nbb number of bits needed

       \note may be reimplemented
    */
    //  Send a nbits  bits command to the PIC
    virtual void send_bits( unsigned int d, int nbb );  ///< Send a nbb bits word to the PIC

    /**
       Send a 6 bits command to the PIC

       \param d The command to be sent
       
       \note may be reimplemented
    */
    //  Send a 6  bits command to the PIC
    virtual void send_cmd( unsigned int d);  ///< Send a nbits bits command to the PIC

    /**
       Returns the PIC memory buffer and the location of code, EEPROM, ID data and config flags

       \param mem Start address of the memory buffer
       \param mem1 Index of the first word of code
       \param mem2 Index of the last word of code
       \param ee1  Index of the first word of EEPROM data
       \param ee2  Index of the last word of EEPROM data
       \param id1  Index of the first word of user ID data
       \param id2  Index of the last word of user ID data
       \param cf1  Index of the first word of config flags
       \param cf2  Index of the last word of config flags
       \param oscback1  Index of the first word of calibation data
       \param oscback2  Index of the last word of  calibation data

      \note There is no identified reason to reimplement this method
    */
    // PIC memory buffer mapping
    virtual void getBuffers(FragBuffer **mem,
                            int& mem1, int& mem2,
                            int& ee1,  int& ee2,
                            int& id1,  int& id2,
                            int& cf1, int& cf2,
                            int& oscback1 ,
                            int& oscback2 ) ;  ///< Mapping of the memory buffer


    // high level programming algorithms
    /**
    	Verify the PIC's memory against memory buffer.

     \param out The output steam for error messages. (see the warning() function)

     \return Error code obtained by ORing the following values :
    -	0 == No error detected
    -	1 == program memory error
    -	2 == EEPROM data error
    -	4 == ID error
    -	8 == config fuses error
    -	64 == calibration word error

     \note Must be implemented. this algorithm depends on the PIC model you have to program,
     but if you are a little bit lucky, the code you need is already in one of the picxxx
     class. If this is the case you just have to inherit them (see pic16F874 or pic12C671 for example).
     If not, you can however use these codes as a base for your developments.

    */
    virtual int verify_pic(ostream& out) = 0 ;  ///< Verify the PIC's memory against memory buffer.
    /**
      Equivalent to    verify_pic(cout)
    */
    int verify_pic() ;   ///< Verify the PIC's memory against memory buffer.

    /**
     	Program  memory  buffer into the PIC

     \param out The output steam for error messages. (see the warning() function)

     \return   Error code obtained by ORing the following values :
     -	0 == OK
     -	1 == programm memory error
     -	2 == EEPROM data error
     -	4 == ID error
     -	8 == config fuses error
     -	64 == calibration word error
     \note Must be implemented. this algorithm depends on the PIC model you have to program,
     but if you are a little bit lucky, the code you need is already in the pic16F84 or pic12C508
     class. If this is the case you just have to inherit them (see pic16F874 or pic12C671 for example).
     If not, you can however use these codes as a base for your developments.

       Standard programming algorithms have been developed for
     - 	12 bits devices (see class  pic12C508)
     - 	14 bits devices (see class pic16FXX or class pic16C6_7_9XX)
     - 	16 bits devices (see class pic18F242)
     so you have  many chances to do not need to write new code : You will have to inherit
     from one of these classes.
     Special case are devices such as 12F675 because, despite its name it is a 14
     bit device, and also because it have special needs about bits or word
     preservation during erasing or programming processes.

    */
    virtual int program_pic(ostream& out) = 0 ; ///< Program  RAM buffer into the PIC

    /// Equivalent to program_pic(cout)
    int program_pic() ;  ///< Program  RAM buffer into the PIC device

    /**
      Read the PIC memory and fill the memory buffer.
     \note Must be implemented. this algorithm depends on the PIC model you have to program,
     but if you are a little bit lucky, the code you need is already in the pic16F84 or pic12C508
     class. If this is the case you just have to inherit them (see pic16F874 or pic12C671 for example).
     If not, you can however use these codes as a base for your developments.

     No error detection for this function
     */
    virtual void read_pic() = 0 ; ///< Read the PIC memory and fill the memory buffer.


    /**
     erase all the pic data : program + EEPROM + ID info + config fuses
     \return
     -0 == OK
     -16 == Non applicable (OTP device)

      \note Must be implemented. this algorithm depends on the PIC model you have to program,
      but if you are a little bit lucky, the code you need is already in another pic class.
      If this is the case you just have to inherit from it (see pic16F874 for example).
      If not, you can however use this code as a base for your own developments.

      Standard erasing algorithms have been developed for
      - 	12 bits devices (see class  pic12C508)
      - 	14 bits devices (see class pic16FXX or class pic16C6_7_9XX)
      - 	16 bits devices (see class pic18F242)
      so you have  many chances to do not need to write new code : You will have to inherit
      from one of these classes.
      Special case are devices such as 12F675 because, despite its name it is a 14
      bit device, and also because it have special needs about bits or word
      preservation during erasing or programming processes.

     */
    virtual int erase_pic() = 0 ;  ///< Erase all the informations programed into the device

    /**  List all PIC models that may be programmed by this class
    \return A blank separated list of PIC names. Please use gpasm-compatible names.
    \note Must be implemented.
    This example comes from the pic12F675 class

    \code
    const char * pic12F675::names()
    {
        return "p12f675 p12f629" ;
    }
    \endcode

    */
    //  List all PIC models that may be programmed using this class
    virtual const char *names()  ; ///<  List all PIC models that may be programmed using this class

    /**
      Word Mask
      \return A word mask for the current PIC model (ie: 0x3FFF for 14 bits devices,
      0xFFF for 12 bits devices, 0XFFFF for 16 bits devices).
      The default implementation returns 0x3FFF
      \note May be reimplemented
    */
    virtual int wmask() ; ///<  Word Mask

    /**
    Config word description
    \return A litteral  describing the config word mapping. This description is a blanks separated
    list of numbered pin names. Each pin name has one the following formats :

    - PPP[nn] or PPP(nn) : The bit nn is named PPP
    - PPP[nn-mm] : the bits from nn to mm are named PPP-(nn-mm) to PPP-0   (nn>=mm)
    - PPP(nn-mm) : the bits from nn to mm are all named PPP   (nn>=mm)

    As usual, you can use /PPP to show that the PPP bit uses negative logic.
    Use the special name "0" if a bit is not programmable and must be forced to 0,
    "1" if a bit is not programmable and must be forced  to 1,
    "X" if a bit is not programmable and must be left unchanged (ie: as it is specified
    by the CONFIG directive).
    If bit name is "0", "1" or "X", the bit editing is disabled at the GUI level.

    This example comes from the pic16F628 class
    \code
    const char * pic16F628::config_bits()
    {
       return "CP[13-12] CP[11-10] 1[9] CPD[8] LVP[7] BODEN[6] MCLRE[5]"
              " FOSC-2[4] /PWRTE[3] WDTE[2] FOSC[1-0]" ;
    }
    \endcode

    \note Must be reimplemented.
    This  function is used by the graphical interface  included in pikdev,
    but is currently not useful for the text-only version of programming software.

    */
    //   Config word description for the current PIC device
    virtual const char *config_bits() ; ///< Config word description for the current PIC device


    /**
      Word size of current PIC flavor (in bits)
      \return Number of bits of the current PIC (12 or 14 or 16)
      \note This function is based on the wmask() function, so it does not need
      to be reimplemented
    */
    //   Word size of the current PIC flavor (in bits)
    int wwidth() ; ///< Word size of the current PIC flavor (in bits)

    //
    // High level load/save routines
    //
    /**
      High level hexfile load function
      \param filename The name of the local (.hex) file to load.
      \return A NULL pointer if  success, else a pointer to an error message
    */
    const char *loadHexFile( const char *filename) ;  ///<  High level hexfile load
    /**
      High level hexfile save function
      \param filename The name of the local (.hex) file to save.
      \param format A gpasm-compatible format name.
        - "inhx8m" for 8 bit format
        - "inhx16" for 16 bits format
        - "inhx32" for 32 bits format
      \return A NULL pointer if  success, else a pointer to an error message
    */
    const char *writeHexFile( const char *filename , const char *format ) ;  ///<  High level hexfile save
    /** DEPRECATED; Will probably be removed from the PiKdev API .

    Deduces the device family from the pic names
    \return 12, 16, 17, 18 (or 0 if picname is not recognized)
    WARNING: Some devices (such as 12F675) look like a 12 bits device, but are in fact
    14 bits devices. 
    */
    int picFamily();
    /** Returns true is this device uses byte adresses (instead of word adresses)
    This is useally the case with 18F devices)
    */
    virtual bool byteAdresses() const ;

    /** Get a word of data from device
    May be reimplemented
    (the default implementation is correct for (most ?) pic12 and pic16 devices but  this
    method is reimplemented for 18 bits devices))
    */
    virtual unsigned int get_word(); ///< Read a data word from the device
    /** Read a byte of data from the device
     May be reimplemented
     The default implementation just call get_word() and is correct 
     for pic12 and pic16 devices
    */
    virtual unsigned int get_byte();
    /**  Get autodetected device name
    \return
    -	"?" if not recognized,
    -	"" if not autodetectable
    -	a standard name such as p16f84a, as they are returned by gpasm -l command.

     Normally, this method should never return "?" as this value corresponds
     to an autodetectable but non supported device.
      */
    virtual std::string deviceID2Name(unsigned int id);
    /** \return  The autodetected device revision number (0 if not autodetectable)  */
    virtual unsigned int deviceID2Revision(unsigned int id);
    /** Device autodetection
    \return The raw type ID of the device (including revision number) or 0 if device is not autodetectable  */
    virtual unsigned int get_deviceID();
    /** Device autodetection support
    \return
    A blanks separated list of pic ID. this list MUST correspond item by item 
    to the names list returned by names(). 
    Each ID of the list is the device ID word in which the revision number is masked.
    Each ID must be specified in hexadecimal.
    If none of the devices returned by names() is autodetectable, IDs() can
    return an empty string. If some devices are not autodetectable, their ID must
    be 0.

    The default implementation returns an empty string, so classes for supporting old non
    autodetectable devices (ie: 16CXXX) do not need to reimplement this method. */
    virtual const char * IDs();

    /** Special locations descriptor
      This method returns a list of special memory locations (in device's memory space)
      The main purpose of this method is to allow the programmer widget to highlight
      some special words.
      Be careful : as special word needs color changes and tooptips allocations/deallocation,
      using too many special words can slow down the programming widget.

      General syntax :
      addresses_range=color{help_text} ...

      adresses_range:
      [low_addr:hi_addr] | unique_addr
      ( unique_addr is a shorthand for [unique_addr:unique_addr], all addresses
      are hexadecimal values )

      color:
      #RRGGBB
      (A HTML-like RGB value)

      help_text:
      Arbitrary long text, without } character. \n are allowed.
      
      =color field can be ommited. In this case, the previous color is used.
      If no color is provided, a default color is used.
      
      {help_text} can be ommited. In this case, the location does not have any
      attached text.

      The method can return several adress specifications, separated by blanks

      example:
      return "[0-3]=#FF0000{first four words} 3FF=#AAAAAA{OSCCAL word}" ;

      \return A blank separated list of locations descriptor.
      Must return an empty string (ie:"") if not used.
      Never return a NULL pointer !

     */
    virtual const char* SPLocs();
    /** No descriptions */
    virtual const char * progOptions();
    /** No descriptions */
    int setProgOptionNumber(int opt);
    /**
        Read the currently plugged device, an translate
        its ID to device name.
        The default implementation of read_device_name() is correct
        for all devices supported by pikdev programming engine,
        so this function just needs to be reimplemented when and
        external  programming software (picp, pk2, etc..) is used


        \return
          - "" : id unavailable
          - "?" : id is available, but device is not supported by the
                  current programming engine
          - else, the chip name with lowercase chars (ie: p16f628)

        \note
        This is an attempt to provide an uniform programming interface
        for both internal (ie:pikdev) and external programming software.

    */
    virtual string read_device_name();
    /**
    Returns the currently plugged device revision number
    as a string.
    When the device is not autodetectable, must return
    an empty string ("")
    \sa  read_device_name 
    */
    virtual string read_device_revision();
    /** No descriptions */
    virtual const char* name();
    bool instanciate(const char *devname);
    /** No descriptions */
    virtual void postInstanciate(const char *devicename);
    /** No descriptions */
    unsigned int& operator[](unsigned int index);
    /** Returns specific ID of this device
    or 0 if device is not autodetectable or instanciation has failed. */
    unsigned int specificID();
    /** true if the device supports autodetection
    */
    bool isDetectable();
    /** No descriptions */
    const char *specificName() ;
    /** Connect a progress handler for displaying current   progress status
    of device programming. */
    void setProgressListener( progressListener *pl );
    /** init progress status from various memory sizes

    return the total step to be performed. 
    Note: Might be wrong in some special cases : just reload
    process_status with the correct value */
    int initProgress();
    /** return device core size (ie: 12, 14 or 16) */
    virtual int deviceCore();
} ;


#endif
