/*
 * プロセスデータのハンドリングを行うクラス
 * ProcessDataBuffer.cpp
 *
 *  Created on: 2018/09/12
 *      Author: shingo
 */

#include <EcOs.h>
#include <ti/drv/uart/UART_stdio.h>

#include "ProcessDataBuffer.h"
#include "FwAppError.h"

//
#define BUFF_FREE (0)
#define RX_BUFF_RX_START (1)
#define RX_BUFF_RX_END (2)
#define RX_BUFF_MOVE_START (3)
#define RX_BUFF_MOVE_END     (4)
#define TX_BUFF_SND_START (11)
#define TX_BUFF_SND_END (12)
#define TX_BUFF_MOVE_START  (13)
#define TX_BUFF_MOVE_END    (14)


/**
 * コンストラクタ
 */
ProcessDataBuffer::ProcessDataBuffer()
{
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            processRx[idx] = NULL;
            rxDataState[idx] = BUFF_FREE;
    }
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            processTx[idx] = NULL;
    }
}

/**
 * デストラクタ
 */
ProcessDataBuffer::~ProcessDataBuffer()
{
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if (processRx[idx] != NULL) {
            OsFree(processRx[idx]);
        }
    }
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if (processTx[idx] != NULL) {
            OsFree(processTx[idx]);
        }
    }

}

/**
 * プロセスデータの送受信用メモリの初期化を行う。
 * @param aInSize 受信用プロセスデータバッファサイズ
 * @param aOutSize 送信用プロセスデータバッファサイズ
 */
void ProcessDataBuffer::init(EC_T_WORD aInSize, EC_T_WORD aOutSize) {
   void* tmpPt;

   rxBufferByteSize = aInSize;
   for (int idx = 0; idx < BUFFER_COUNT; idx++) {
       tmpPt = OsMalloc(aInSize);
       if (tmpPt != NULL) {
           processRx[idx] = (EC_T_BYTE*)tmpPt;
           rxDataState[idx] = BUFF_FREE;
       } else {
           UART_printf("プロセスデータ送受信用エリアの取得に失敗！！\n");
           rxDataState[idx] = BUFF_FREE;
       }
   }

   txBufferByteSize = aOutSize;
   for (int idx = 0; idx < BUFFER_COUNT; idx++) {
       tmpPt = OsMalloc(aInSize);
       if (tmpPt != NULL) {
           processTx[idx] = (EC_T_BYTE*)tmpPt;
           txDataState[idx] = BUFF_FREE;
       } else {
           UART_printf("プロセスデータ送送信用エリアの取得に失敗！！\n");
       }
   }
}

// ------------  受信用バッファ管理 -------------------------
/**
 * プロトコルスタックに渡すための、入力データの受信用バッファを取得する。
 * 受信後は、受信完了通知メソッドを呼び出す事。
 * @retuen 受信バッファアドレス
 */
EC_T_BYTE* ProcessDataBuffer::getRxFreeBuffer() {
    EC_T_BYTE* retVal = NULL;

    // 何れかのバッファが送信中の場合に送信開始は想定外
    if ((rxDataState[0] == RX_BUFF_RX_START) || (rxDataState[1] == RX_BUFF_RX_START)) {
        FwAppError::printError(FW_ERROR_0012);
        return NULL;
    }

    // 未使用領域を探す
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if( rxDataState[idx] == BUFF_FREE) {
            retVal =  processRx[idx];
            rxDataState[idx] = RX_BUFF_RX_START;
            break;
        }
    }

    if (retVal == NULL) {
        // ホストに転送完了したバッファを探す
        for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            if( rxDataState[idx] == RX_BUFF_MOVE_END) {
                retVal =  processRx[idx];
                rxDataState[idx] = RX_BUFF_RX_START;
                break;
            }
        }
    }

    if (retVal == NULL) {
        // 前回受信完了しているバッファを探す
        for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            if( rxDataState[idx] == RX_BUFF_RX_END) {
                retVal =  processRx[idx];
                rxDataState[idx] = RX_BUFF_RX_START;
                break;
            }
        }
    }

    if (retVal == NULL) {
        FwAppError::printError(FW_ERROR_0009);
    }

    return retVal;
}

/**
 * プロセス入力データの受信完了を通知する
 * @param aInAddr 受信したデータ格納アドレス(getRxFreeBuffer()で取得したアドレス）
 */
void ProcessDataBuffer::indRxEnd(EC_T_BYTE* aInAddr) {

    int targetIdx;
    int otherIdx;

     // 完了対象のバッファを判別
     if (processRx[0] == aInAddr) {
         targetIdx = 0;
         otherIdx = 1;
     } else if (processRx[1] == aInAddr) {
         targetIdx = 1;
         otherIdx = 0;
     } else {
         FwAppError::printError(FW_ERROR_0010);
         return;
         // 排他制御時には注意！！
     }

     // 対象バッファの基の状態を確認し、ステータスを変更
      if (rxDataState[targetIdx] != RX_BUFF_RX_START) {
          FwAppError::printError(FW_ERROR_0011);
          return;
          // 排他制御時には注意！！
      } else {
          rxDataState[targetIdx] = RX_BUFF_RX_END;
      }

      // 他方のバッファも受信完了状態ならば、初期状態とする。
      if (rxDataState[otherIdx] == RX_BUFF_RX_END) {
          rxDataState[otherIdx] = BUFF_FREE;
      }
}

/**
 * 受信済みバッファアドレスを取得する。
 * 最新の受信データ無い場合はNULLを返す。
 */
EC_T_BYTE* ProcessDataBuffer::getInDataReceivedBuffer() {
    EC_T_BYTE* retVal = NULL;

    // 何れかのバッファが送信データ設定中の場合の要求は想定外
    if ((rxDataState[0] == RX_BUFF_MOVE_START) || (rxDataState[1] == RX_BUFF_MOVE_START)) {
        FwAppError::printError(FW_ERROR_0013);
        return NULL;
    }

    // 受信済みのバッファを探す
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if( rxDataState[idx] == RX_BUFF_RX_END) {
            retVal =  processRx[idx];
            rxDataState[idx] = RX_BUFF_MOVE_START;
            break;
        }
    }

    // 受信済みの最新が無い場合はNULLでなく、前回値を返す場合はRX_BUFF_MOVE_ENDを探す処理を追加する。

    return retVal;
}

/**
 * 受信バッファの読出しが完了した事を通知する。
 * 本処理により、対象のバッファステータスはRX_BUFF_MOVE_ENDとなる。
 */
void ProcessDataBuffer::indRxMoveEnd(EC_T_BYTE* aInAddr) {

    int targetIdx;
    int otherIdx;

    // 完了対象のバッファを判別
    if (processRx[0] == aInAddr) {
        targetIdx = 0;
        otherIdx = 1;
    } else if (processRx[1] == aInAddr) {
        targetIdx = 1;
        otherIdx = 0;
    } else {
        FwAppError::printError(FW_ERROR_0014);
        return;
        // 排他制御時には注意！！
    }

    // 対象バッファの元の状態を確認し、ステータスを変更
    if (rxDataState[targetIdx] != RX_BUFF_MOVE_START) {
        FwAppError::printError(FW_ERROR_0015);
        return;
        // 排他制御時には注意！！
    } else {
        rxDataState[targetIdx] = RX_BUFF_MOVE_END;
    }

    // 他方のバッファも送信完了状態ならば、初期状態とする。
    if (rxDataState[otherIdx] == RX_BUFF_MOVE_END) {
        rxDataState[otherIdx] = BUFF_FREE;
    }
}



// ------------- 出力データバッファ対応メソッド    --------
/**
 * プロトコルスタックが使用する出力データバッファを取得する。
 * 送信後は、送信完了通知メソッドを呼び出す事。
 * 本処理により、対象のバッファステータスはTX_BUFF_TX_STARTとなる。
 * @retuen 送信（出力データ）バッファアドレス
 */
EC_T_BYTE* ProcessDataBuffer::getTxDataBuffer() {
    EC_T_BYTE* retVal = NULL;
    // 何れかのバッファが送信中の場合に送信開始は想定外
    if ((txDataState[0] == TX_BUFF_SND_START) || (txDataState[1] == TX_BUFF_SND_START)) {
        FwAppError::printError(FW_ERROR_0001);
        return NULL;
    }

    // 送信（出力）データが用意されたバッファを探す
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if( txDataState[idx] == TX_BUFF_MOVE_END) {
            retVal =  processTx[idx];
            txDataState[idx] = TX_BUFF_SND_START;
            break;
        }
    }

    // 新規の送信データが無い場合には、前回の送信済みデータを返す
    if (retVal == NULL) {
        for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            if( txDataState[idx] == TX_BUFF_SND_END) {
                retVal =  processTx[idx];
                txDataState[idx] = TX_BUFF_SND_START;
                break;
            }
        }
    }

    if (retVal == NULL) {
        FwAppError::printError(FW_ERROR_0002);
    }

    return retVal;
}

/**
 * 送信が完了した出力バッファの使用済みを通知する。
 * 本処理により、対象のバッファステータスはTX_BUFF_TX_ENDとなる。
 */
void ProcessDataBuffer::indTxEnd(EC_T_BYTE* aInAddr) {

    int targetIdx;
    int otherIdx;

    // 完了対象のバッファを判別
    if (processTx[0] == aInAddr) {
        targetIdx = 0;
        otherIdx = 1;
    } else if (processTx[1] == aInAddr) {
        targetIdx = 1;
        otherIdx = 0;
    } else {
        FwAppError::printError(FW_ERROR_0003);
        return;
        // 排他制御時には注意！！
    }

    // 対象バッファの基の状態を確認し、ステータスを変更
    if (txDataState[targetIdx] != TX_BUFF_SND_START) {
        FwAppError::printError(FW_ERROR_0004);
        return;
        // 排他制御時には注意！！
    } else {
        txDataState[targetIdx] = TX_BUFF_SND_END;
    }

    // 他方のバッファも送信完了状態ならば、初期状態とする。
    if (txDataState[otherIdx] == TX_BUFF_SND_END) {
        txDataState[otherIdx] = BUFF_FREE;
    }
}

/**
 * 出力データを書き込む、送信バッファアドレスを取得する。
 * 本処理により、対象のバッファステータスはTX_BUFF_MOVE_STARTとなる。
 */
EC_T_BYTE* ProcessDataBuffer::getOutDataWriteBuffer() {
    EC_T_BYTE* retVal = NULL;

    // 何れかのバッファが送信データ設定中の場合の要求は想定外
    if ((txDataState[0] == TX_BUFF_MOVE_START) || (txDataState[1] == TX_BUFF_MOVE_START)) {
        FwAppError::printError(FW_ERROR_0005);
        return NULL;
    }

    // 未使用のバッファを探す
    for (int idx = 0; idx < BUFFER_COUNT; idx++) {
        if( txDataState[idx] == BUFF_FREE) {
            retVal =  processTx[idx];
            txDataState[idx] = TX_BUFF_MOVE_START;
            break;
        }
    }

    // 未使用バッファが無い場合には、送信済みバッファを返す（通常はこのパス）
    if (retVal == NULL) {
        for (int idx = 0; idx < BUFFER_COUNT; idx++) {
            if( txDataState[idx] == TX_BUFF_SND_END) {
                retVal =  processTx[idx];
                txDataState[idx] = TX_BUFF_MOVE_START;
                break;
            }
        }
    }

    if (retVal == NULL) {
        FwAppError::printError(FW_ERROR_0006);
    }

    return retVal;
}

/**
 * 送信バッファへの出力データの書き込みが完了した事を通知する。
 * 本処理により、対象のバッファステータスはTX_BUFF_MOVE_ENDとなる。
 */
void ProcessDataBuffer::indMoveEnd(EC_T_BYTE* aInAddr) {

    int targetIdx;
    int otherIdx;

    // 完了対象のバッファを判別
    if (processTx[0] == aInAddr) {
        targetIdx = 0;
        otherIdx = 1;
    } else if (processTx[1] == aInAddr) {
        targetIdx = 1;
        otherIdx = 0;
    } else {
        FwAppError::printError(FW_ERROR_0007);
        return;
        // 排他制御時には注意！！
    }

    // 対象バッファの元の状態を確認し、ステータスを変更
    if (txDataState[targetIdx] != TX_BUFF_MOVE_START) {
        FwAppError::printError(FW_ERROR_0008);
        return;
        // 排他制御時には注意！！
    } else {
        txDataState[targetIdx] = TX_BUFF_MOVE_END;
    }

    // 他方のバッファも送信完了状態ならば、初期状態とする。
    if (txDataState[otherIdx] == TX_BUFF_MOVE_END) {
        txDataState[otherIdx] = BUFF_FREE;
    }
}

void ProcessDataBuffer::checkTxAddr(EC_T_BYTE* aInAddr) {

    if (aInAddr == processTx[0]) {
        return;
    }
    if (aInAddr == processTx[1]) {
        return;
    }

    UART_printf("Error :TX buffer address !! %lx", aInAddr);


}
