#ifndef __BLACKSASI_H__
#define __BLACKSASI_H__
#include "sdcard.h"
#include "gpio.h"
#include "log.h"
#include "config.h"
#include "scsi.h"
// Log File
#define VERSION "0.xx-SNAPSHOT-BLACKSASI-2022-03-20-F4"

#define LOG_FILENAME "LOG.txt"

#define SPI_CLOCK SD_SCK_MHZ(50)

#define DEBUG            1      // 0:No debug information output
                                // 1: Debug information output to USB Serial
                                // 2: Debug information output to LOG.txt (slow)

#define SCSI_SELECT      0      // 0 for STANDARD
                                // 1 for SHARP X1turbo
                                // 2 for NEC PC98
#define READ_SPEED_OPTIMIZE  1 // Faster reads
#define WRITE_SPEED_OPTIMIZE 1 // Speeding up writes
#define USE_DB2ID_TABLE      1 // Use table to get ID from SEL-DB

// SCSI config
#define NUM_SCSIID  7          // Maximum number of supported SCSI-IDs (The minimum is 0)
#define NUM_SCSILUN 2          // Maximum number of LUNs supported     (The minimum is 0)
#define READ_PARITY_CHECK 0    // Perform read parity check (unverified)

// HDD format
#define MAX_BLOCKSIZE 2048     // Maximum BLOCK size


#if DEBUG == 1
#define serial Serial
#define LOG(XX)      serial.print(XX)
#define LOGHEX(XX)   serial.print(XX, HEX)
#define LOGDEC(XX)   serial.print(XX, DEC)
#define LOGBIN(XX)   serial.print(XX, BIN)
#define LOGN(XX)     serial.println(XX)
#define LOGHEXN(XX)  serial.println(XX, HEX)
#define LOGDECN(XX)  serial.println(XX, DEC)
#define LOGBIN_N(XX) serial.println(XX, BIN)
#elif DEBUG == 2
#define LOG(XX)      LOG_FILE.print(XX); LOG_FILE.sync();
#define LOGHEX(XX)   LOG_FILE.print(XX, HEX); LOG_FILE.sync();
#define LOGDEC(XX)   LOG_FILE.print(XX, DEC); LOG_FILE.sync();
#define LOGBIN(XX)   LOG_FILE.print(XX, BIN); LOG_FILE.sync();
#define LOGN(XX)     LOG_FILE.println(XX); LOG_FILE.sync();
#define LOGHEXN(XX)  LOG_FILE.println(XX, HEX); LOG_FILE.sync();
#define LOGDECN(XX)  LOG_FILE.println(XX, DEC); LOG_FILE.sync();
#define LOGBIN_N(XX) LOG_FILE.println(XX, BIN); LOG_FILE.sync();
#else
#define LOG(XX)       //serial.print(XX)
#define LOGHEX(XX)    //serial.print(XX, HEX)
#define LOGDEC(XX)    //serial.print(XX, DEC)
#define LOGBIN(XX)    //serial.print(XX, BIN)
#define LOGN(XX)      //serial.println(XX)
#define LOGHEXN(XX)   //serial.println(XX, HEX)
#define LOGDECN(XX)   //serial.println(XX, DEC)
#define LOGBIN_N(XX)  //serial.println(XX, BIN)
#endif

#define active   1
#define inactive 0
#define high 0
#define low 1

#define isHigh(XX) ((XX) == high)
#define isLow(XX) ((XX) != high)

// GPIO register port
#define PAREG GPIOA->regs
#define PBREG GPIOB->regs
#define PCREG GPIOC->regs
#define PDREG GPIOD->regs
#define PEREG GPIOE->regs

// Termination control (LOW is active)
#define TERMINATION_HIGH()      GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_HIGH % 16) << 16 | (1 << BOARD_SCSI_TERM_LOW % 16);
#define TERMINATION_LOW()       GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_LOW % 16) << 16 | (1 << BOARD_SCSI_TERM_HIGH % 16);
#define TERMINATION_OFF()       GPIOREG(BOARD_SCSI_TERM_HIGH)->BSRR = (1 << BOARD_SCSI_TERM_HIGH % 16) | (1 << BOARD_SCSI_TERM_LOW % 16);

// Enable SCSI buffers
#define SCSI_OUTPUT_ENABLE()    GPIOREG(BOARD_TRANS_OE)->BSRR = (1 << (BOARD_TRANS_OE % 16)) << 16;
#define SCSI_OUTPUT_DISABLE()   GPIOREG(BOARD_TRANS_OE)->BSRR = (1 << (BOARD_TRANS_OE % 16));

// SCSI Data Direction
#define SCSI_DATABUS_IN()       GPIOREG(BOARD_SCSI_DTD)->BSRR = (1 << (BOARD_SCSI_DTD % 16)) << 16; 
#define SCSI_DATABUS_OUT()      GPIOREG(BOARD_SCSI_DTD)->BSRR = (1 << (BOARD_SCSI_DTD % 16))

// SCSI IND Direction
#define SCSI_IND_IN()           GPIOREG(BOARD_SCSI_IND)->BSRR = (1 << (BOARD_SCSI_IND % 16));
#define SCSI_IND_OUT()          GPIOREG(BOARD_SCSI_IND)->BSRR = (1 << (BOARD_SCSI_IND % 16)) << 16;

//SCSI Data and IND Direction
#define SCSI_DATABUS_IND_IN()   GPIOREG(BOARD_SCSI_DTD)->BSRR = ((1 << (BOARD_SCSI_DTD % 16)) << 16) | (1 << (BOARD_SCSI_IND % 16));
#define SCSI_DATABUS_IND_OUT()  GPIOREG(BOARD_SCSI_DTD)->BSRR = ((1 << (BOARD_SCSI_IND % 16)) << 16) | (1 << (BOARD_SCSI_DTD % 16));

// SCSI TAD Direction

#define SCSI_TAD_IN()           GPIOREG(BOARD_SCSI_TAD)->BSRR = (1 << (BOARD_SCSI_TAD % 16));
#define SCSI_TAD_OUT()          GPIOREG(BOARD_SCSI_TAD)->BSRR = (1 << (BOARD_SCSI_TAD % 16)) << 16;

// Virtual pin (Arduio compatibility is slow, so make it MCU-dependent)
#define PA(BIT)       (BIT)
#define PB(BIT)       (BIT + 16)
#define PC(BIT)       (BIT + 32)
#define PD(BIT)       (BIT + 48)
#define PE(BIT)       (BIT + 64)

// Virtual pin decoding
#define GPIOREG(VPIN)     ((VPIN) >= 16 ? ((VPIN) >= 32 ? ((VPIN) >= 48 ? ((VPIN) >= 48 ? PEREG : PDREG) : PCREG) : PBREG) : PAREG)
#define BITMASK(VPIN)     (1 << ((VPIN % 16) & 15))

#define vATN       PB(14)     // SCSI:ATN
#define vBSY       PB(6)      // SCSI:BSY
#define vACK       PB(7)      // SCSI:ACK
#define vRST       PA(15)     // SCSI:RST
#define vMSG       PE(2)      // SCSI:MSG
#define vSEL       PE(3)      // SCSI:SEL
#define vCD        PE(4)      // SCSI:C/D
#define vREQ       PE(5)      // SCSI:REQ
#define vIO        PE(6)      // SCSI:I/O
#define vSD_CS     PB(1)      // SDCARD:CS
#define vDTD       PC(0)      // SCSI:DTD
#define vIND       PC(1)      // SCSI:IND
#define vTAD       PC(2)      // SCSI:TAD
#define vTRANS_OE  PB(12)     // SCSI:TRANS_OE
// SCSI output pin control: active LOW (direct pin drive)
#define SCSI_OUT(VPIN,ACTIVE) { GPIOREG(VPIN)->BSRR = BITMASK(VPIN) << ((ACTIVE) ? 16 : 0); }

// SCSI input pin check (inactive=0,active=1)
#define SCSI_IN(VPIN) ((~GPIOREG(VPIN)->IDR >> ((VPIN % 16) & 15)) & 1)

// HDDiamge file
#define HDIMG_ID_POS  2                 // Position to embed ID number
#define HDIMG_LUN_POS 3                 // Position to embed LUN numbers
#define HDIMG_BLK_POS 5                 // Position to embed block size numbers
#define MAX_FILE_PATH 32                // Maximum file name length

// SCSI
#define SCSI_INFO_BUF_SIZE 36
#define SCSI_INFO_VENDOR_SIZE 9
#define SCSI_INFO_PRODUCT_SIZE 17
#define SCSI_INFO_VERSION_SIZE 5
typedef struct hddimg_struct
{
	FsFile      m_file;                 // File object
	uint64_t    m_fileSize;             // File size
	size_t      m_blocksize;            // SCSI BLOCK size
}HDDIMG;

// Declare functions
void onFalseInit(void);
void onBusReset(void);
void switchImage(void);
void initFileLog(void);
void finalizeFileLog(void);

#endif // __BLACKSASI_H__