#ifndef LCF_FATHER_H_
#define LCF_FATHER_H_
#include "myXPU.h"
#include "myHead.h"
#include "myType.h"
#include "tensor.h"
#include "globalMemPoolFloat.h"
#include "memBasicManage.h"
#include "memoryHandle.h"
#include "matUtility.cuh"
#include "errOutput.h"
#include "winLinux.h"
#include "strUtility.h"
#include "optimizer.h"
#include "batchSeqComp.h"
#include "devicesBatchDispatch.h"

#include "fp816Base.cuh"
#include "fp816LocalMem.cuh"

class TensorOperate {

    public:
        TensorOperate();
        virtual ~TensorOperate();
        void SetMemberInitStatus();
        void SetInYTensor(TensorOperate **pPointToInYTensor, int num);
        void SetOutYTensor(TensorOperate **pPointToOutYTensor, int num);
        TensorOperate *GetPointInYTensorOperate(int index);
        TensorOperate *GetPointOutYTensorOperate(int index);
        void SetInYFromByTensorOperateID(int *inDataID, int iNum);
        int GetInYNum();
        int GetOutYNum();
        int LayerIfMyInDataLayer(TensorOperate *chkLayer);
        void CreateSaveInYOutDPoint();

        virtual void ForwardBeforeAction(int devID) {};
        virtual void ForwardAfterAction(int devID) {};
        virtual void BackwardBeforeAction(int devID) {};
        virtual void BackwardAfterAction(int devID) {};
        void Forward();
        void Backward();
        virtual void CompuateOutY(int devID, int seqNum) {};
        virtual void CompuateOutD(int devID, int seqNum) {};
        void Forward(int devID, int seqNum);
        void Backward(int devID, int seqNum);
        void BackwardManual(int devID, int seqNum, int ifSendDToNext);
        
        virtual void SaveLayerWeight(int n) {};
        virtual void LoadLayerWeight() {};
        virtual void UpdateWeightMain() {};
        
        virtual void ComputerYMemRealease(int devID) {};
        virtual void ComputerDMemRealease(int devID) {};
        
        virtual void CompGrad(int devID, int seqNum){};
        virtual void ComputeAndCreateOutYInDTensorInfo() {};
        virtual void CreateParaAndInstance() {};

        TensorInfo *GetOutYInDTensorInfo(int devID);
        TensorInfo *GetOutYInDTensorInfoGroupInner(int devID);
        TensorInfo *GetInYOutDTensorInfo(int devID, int index);
        TensorInfo *GetInYOutDTensorInfoManual(int devID, int index);
        
        void OneUnitInYArrive(int devID, int seqNum, TensorOperate *outYTensor, MemoryHandle *pMemoryHandle);
        void OnUnitInDArrive(int devID, int seqNum, MemoryHandle *pMemoryHandle);
        void OneUnitInYArrive(int devID, int seqNum, int index, MemoryHandle *pMemoryHandle);
        void SendOutYTensorToAllNextTensor(int devID, int seqNum);
        void SendOutDTensorToAllNextTensor(int devID, int seqNum);
        void SendOutDTensorToAllNextTensor(int devID, int seqNum, int inIdex);
        int GetInYCompuateIndex(TensorOperate *outYTensor);
        void BuildOutDataLayersFromSubGroup();
               
        FILE *GetSaveWeightBinaryFileHandle(int n);
        FILE *GetLoadWeightBinaryFileHandle();
        void SaveWeightValue(FILE *fp, int inID, int inType, float *wData, long long len);
        struct WeightBlob *LoadWeightValue(FILE *fp, int inID, long long needNum);
        int GetCurrentFinishRecvYCount();
        
        void SubLayersSaveWeight(int n);
        void SubLayersLoadWeight();
        void SubLayersComputerYMemRealease(int devID);
        void SubLayersComputerDMemRealease(int devID);
        void SubLayersCreateParaAndInstance();
        void SubLayersCreateOutYInDTensorInfo();
        void SubLayersCreateSaveInYOutDPoint();
        void SubLayersBackward(int devID, int idx);
        void SubLayersForward(int devID, int idx);
        void SubLayersUpdateWeightMain();
        void SubLayersSetPoolAndRunMethod();
        void SubLayersTakeIn(TensorOperate *pSub);
        
        GlobalMemPoolFloat* GetMainParaGlobalMemList();
        GlobalMemPoolFloat* GetBackParaGlobalMemList();
        GlobalMemPoolFloat* GetCompParaGlobalMemList();
        GlobalMemPoolFloat *GetMainParaGlobalMem(int devID);
        GlobalMemPoolFloat *GetBackParaGlobalMem(int devID);
        GlobalMemPoolFloat *GetCompParaGlobalMem(int devID);
        void SetDeviceGlobalMemPool(int num, unsigned char *devID, GlobalMemPoolFloat *iMain, GlobalMemPoolFloat *iBack, GlobalMemPoolFloat *iCMem);
        
        int GetUID();
        void SetID(int iID);
        int GetID();
        char *GetIDCode();
        void SetIDCode(const char *inCodeStr);

        void ResetForOneRecycle();

        void RealeaseInOutYDTensorData();
        void CreateInYParaSaveTensorData(int devID, int seqNum, int index);
        TensorData *GetSaveInYParaTensorData(int devID, int seqNum, int index);
        void RealeaseOneInYParaSaveTensorData(int devID, int seqNum, int index);

        void CreateOutYCMemSaveTensorData(int devID, int seqNum);
        TensorData *GetSaveOutYCMemTensorData(int devID, int seqNum);
        void RealeaseOneOutYCMemSaveTensorData(int devID, int seqNum);
        
        TensorData *GetSaveOutYParaTensorData(int devID, int seqNum);
        void CreateOutYParaSaveTensorData(int devID, int seqNum);
        void RealeaseOneOutYParaSaveTensorData(int devID, int seqNum);
        
        TensorData *GetSaveOutDCMemTensorData(int devID, int seqNum, int index);
        void CreateOutDCMemSaveTensorData(int devID, int seqNum, int index);
        void RealeaseOneOutDCMemSaveTensorData(int devID, int seqNum, int index);
        
        TensorData *GetSaveOutDParaTensorData(int devID, int seqNum, int index);
        void CreateOutDParaSaveTensorData(int devID, int seqNum, int index);
        void RealeaseOneOutDParaSaveTensorData(int devID, int seqNum, int index);
                
        TensorData *GetSaveInDParaTensorData(int devID, int seqNum);
        void CreateInDParaSaveTensorData(int devID, int seqNum);
        void RealeaseOneInDParaSaveTensorData(int devID, int seqNum);
        
        int CheckExistSaveInDParaTensorData(int devID, int seqNum);
        int CheckExistSaveOutDCMemTensorData(int devID, int seqNum, int index);
        int CheckExistSaveInYParaTensorData(int devID, int seqNum, int index);
        
        void TakeBackCMemToPool(int devID, MyVoid *iCMem);
        MemoryHandle *CreateCMemHandle(int ifType, int devID, int floatSize);
        void DestoryCMemHandle(MemoryHandle *pHandle);
        MemoryHandle *GetHandleCMem(int ifType, int devID, MemoryHandle *pHandle);
        MemoryHandle *GetHandleCMem(int ifType, int devID, MemoryHandle *pHandle, int ofs, int useSize);             
        MemoryHandle *GetHandleCMem(int ifType, int devID, MemoryHandle *pHandle, int mHID);
        MemoryHandle *GetHandleNewCMem(int devID, MemoryHandle *pHandle, int ofs, int useSize);
        
        void CopyFromHandleTwoFloatType(int devID, MemoryHandle *dst, MemoryHandle *src);
        void CopyFromHandleTwoFloatType(int devID, MemoryHandle *dst, MemoryHandle *src, int dOffset, int sOffset, int len);
        void AddinHandleTwoFloatType(int devID, MemoryHandle *dst, MemoryHandle *src);
        
        MyVoid *GetVoidByCMemAndSetFloat(int ifType, int devID, float f);
        
        void SetSampleLabelMaster(int type);

        int GetRunMethod();
        void SetRunMethod(int iMethod);
       
        int GetIfLoadWeight();
        void SetIfLoadWeight(int ifLoadWeight);
        
        LayerType GetLayerType();
        void SetInYInfoManual(int iDims, int *iDimSize);
        void SetInYInfoManual(TensorInfo *iTensorInfo, int num);
        
        void CreateSaveYDInstance(); 
                 
        void UpdateIterNum();
        void SetLamdaAndAlpha(float iLambda, float iAlpha);
        void SetLrPara(float iLrBase, float iLrDecay, int iLrStep);
        float GetLrBase();
        float GetLrDecay();
        int GetLrStep();
        float GetCurrentLearnRate();
        void SetClipL2Norm(float iNorm);                                 // Set clip value
        float GetClipL2Norm();
        MemType GetCMemType(int devID);
        float alpha;                                                     // momentum coff
        float lambda;                                                    // L2 coff
        
        LayerType mLayType;
        int mIfSaveOutY, mIfSaveOutD;
        
        TensorInfo *mOutYInDInfo, *mOutYInDInfoGroupInner;

        int *mPointToInYTensorID;  
        TensorInfo *mInYInfoManual;
                
        static char mWeightFileName[CONST_STR_LEN];      
        
        void SaveData(const char *fName, float *data, int len, int num); 
        void CreateIDCode();      
        int GetInGroupIdx(TensorOperate *pSub);
        
        virtual int ChechIfEndThisRecycle(int i) {return 0;};
    
        void SetBatchSeqConfig(int devID, struct BatchSeqStruct *pBatchSeqStruct);
        BatchSeqComp mBatchSeqComp;
        
        unsigned char *GetDeviceIDList();
        int GetDevicesNum();
        
        void SetAllDevicesMaxSeqNum(int iMaxSeqNum, DevicesBatchDisptach *iDevicesBatchDisptach);
        void SetAllDevicesOutputTensorDataInfo(int dims, int *dimInfo);
        void SetAllDevicesGroupInnerTensorDataInfo(int dims, int *dimInfo);
        int  GetParaDevID();

        void DEVICES_LOCK();
        void DEVICES_UNLOCK();

        DevicesBatchDisptach *GetDevicesBatchDisptach();
        
        int fType;      
        
    private:
        int mDevicesNum;
        unsigned char *mDeviceId;
        GlobalMemPoolFloat *mMainParaGlobalMem;
        GlobalMemPoolFloat *mBackParaGlobalMem;
        GlobalMemPoolFloat *mCompGlobalMem;
        
        void BuildOutDataLayers(TensorOperate *resultLayer, TensorOperate *pLayer);       
        void RealeaseTensorOperateMemory();
        void ReLocationOutYByLabelSeq(int devID, int seqNum);
        void ReLocationInDByLabelSeq(int devID, int seqNum);
    
        int mInYNum;
        TensorOperate **mPointToInYTensor;
        
        int mOutYNum;
        TensorOperate **mPointToOutYTensor;
        
        TensorData **mSaveInYParaData;  
        TensorData **mSaveOutYCMemData;  
        TensorData **mSaveOutYParaData;
        TensorData **mSaveOutDCMemData;
        TensorData **mSaveInDParaData;
        TensorData **mSaveOutDParaData;
                         
        TensorOperate **mSubLayer;
        int mSubLayerNum;

        TensorOperate *mGroupMaster;

        DevicesBatchDisptach *mDevicesBatchDisptach;
                                     
        int mRecvYCount;
        int mRunMethod;
        int mID;
        char mIDCode[128];
        
        float mClipL2Norm;                                               // Clip norm commonly set 1.0F
        float mLrBase;                                                   // Base learn rate
        float mLrDecay;                                                  // Learn decay
        int mLrStep;                                                     // the step of decay
        int mIterUpdateNum;
      
        static int mUID;    
        int mIfLoadWeight;        
    
        #ifdef USE_OPEN_MP    
        omp_lock_t mLock;
        #endif  
};

#endif
