package main

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	pb "github.com/hyperledger/fabric/protos/peer"
	"reflect"
	"strconv"
	"time"
)

type GXHCC struct {
}

type AuthGroup struct {
	Users map[string]int64 `json:"users"`  //用户组权限，默认使用用户组。我们使用形如：“User1”:timestamp. key 表示用户或者角色，value 表示有效时间。我们使用格林威治时间时间戳
	Roles map[string]int64 `json:"roles"`  //角色组权限，
}


type Auth struct {
	Read  AuthGroup `json:"read"` // 字段的读权限,“”,不存在权限，
	Write AuthGroup `json:"write"`//字段的写权限
}


type SchemaParameters struct {
	Value interface{}  `json:"val"`//默认值  这里特别说明一下如果这里是backUp 字段，我们不进行更深一步的校验，包括格式
	PAuth  Auth         `json:"auth"`
	FCheck *Checks		`json:"fcheck"`
}

type Checks struct {
	FType 		string  `json:"ftype"` //字段类型
	Must  		bool 	`json:"must"`	 //是否必填
	Fieldlen  	int 	`json:"fieldlen"` //字段长度（如果字段类型是string,表示字段长度，如果是数值类型用0,1表示数据正负）
}

type Schema struct {
	Fields 		map[string]SchemaParameters `json:"fields"`
	SchemaAuth 	Auth `json:"schema_auth"`//schema 的写权限，主要是用在 schema update 部分
	SAuth 		Auth `json:"auth"`
}


type AlibusiLeadger struct {
	Id 		string `json:"id"`  //机构Id
	Abail   float64 `abail`  //剩余保证金
	Amount  float64 `amount`  //可提取资金，在当前只包括预付金部分的增加，分成在后续部分记录
}

type MerchantsLeadger struct {
	Id 			string  `json:"id"`  //机构Id
	Bid         string   `json:"bid"`
	Regular 	float64 `json:"regular"` //剩余预付金
	Rregular 	float64 `json:"rregular"` //实际预付金
}

type CouticketsLeadger struct {
	Id 			string `json:"id"`     //券ID
	Codenum     int `json:"codenum"`    //码总数
	Codestork	int `json:"codestork"`  //码库存
}

type CoutcantmLeadger struct {
	Id 		string `json:"d"`  //可用门店Id
	Tid 		string `json:"tid"`  //券id
	Codenum		int    `json:"codenum"` //可用码数
	Resid 		int	   `json:"resid"`  //剩余码数
}

type Merchantsale struct {
	Mid 	  string  `json:"mid"`  //商户Id
	Tamount   float64  `json:"tamount"` //交易金额
	Treposit  float64  `json:"treposit"` //扣除预付金
	Bereposit float64  `json:"bereposit"` //扣除前预付金
	Afreposit float64  `json:"afreposit"` //扣除后预付金
	Feerate   float64  `json:"feerate"`   // 手续费扣率
	Fee		  float64  `json:"fee"`    // 手续费
	Oamount   float64  `json:"oamount"` // 原始交易金额
	Adiscounts float64 `json:"adiscounts"` //代理结算折扣
	Sdiscounts float64 `json:"sdiscounts"` //消费折扣
	Nodisamount float64 `json:"nodisamount"` //不打折金额
	Disamount   float64 `json:"disamount"`  //打折金额
	Perquisites float64 `json:"perquisites"` //额外收益
}

type Couticketorders struct {
	Orderid  		string `json:"orderid"`  //订单ID
	Oamount			float64`json:"oamount"`  //原始金额
	Pamount			float64`json:"pamount"`  //支付金额
	Mid 			string `json:"mid"`      //门店Id
	Codenum			float64`json:"codenum"`  //券码数量
	Adiscount		float64`json:"adiscount"`//券结算折扣
	Norate			float64`json:"norate"`   //无预付金收益率
	Outfree			float64`json:"outfree"`  //付款方手续费
	Infree			float64`json:"infree"`	 //收款方手续费
	Partnersfree	float64`json:"partnersfree"` //订单合作方手续飞
	Free	  		float64`json:"free"`  //总手续费

}
type Profit struct {
	Id 		string `json:"id"`  //交易ID/或者订单ID
	Type    int    `json:"type"` //交易还是订单标识 0 表示商户交易 1表示订单交易
	profit  float64`json:"profit"` //利润
}

var (
	defValue = "***" //查询用户权限不足的情况下默认返回值
	PREFIX = "GXH"
	IsUsers = false   //是否启用用户权限，false 表示启用Roles
)

// Init does nothing for this cc
func (t *GXHCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success([]byte("GXHCC init successful! "))
}

// Invoke for this chaincode exposes
func (t *GXHCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	// get arguments and transient
	functionName, args := stub.GetFunctionAndParameters()

	switch functionName {
	case "authority":

	case "schema":
		res, err := SchemaProcess(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("SchemaProcess function err: %s", err))
		}
		return shim.Success([]byte(res))

	case "put":
		res, err := put(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("put data function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "get":
		res, err := get(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("get data function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "update":
		res, err := update(args, stub)
		if err != nil {
			return shim.Error(fmt.Sprintf("update data function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "InvokeTable":
		return InvokeTable(args,stub)
	default:
		return shim.Error(fmt.Sprintf("Unsupported function %s", functionName))
	}
	return shim.Success([]byte("GXHCC invoke successful "))
}

/*
	put
	args 字段说明：
	1：args[0]代表我们具体操作的表，
    2：args[1:]后面的数据表示要记录到数据库的数据,每一个string 必须是json字符串，并且需要每一个json 字符串都必须包含id 字段

*/
func put(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) < 2 {
		return "", fmt.Errorf("put data operation expected more than 2 parameters! ")
	}
	var PutDatas []map[string]interface{}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	schema := &Schema{}
	result, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(result), &schema); err != nil {
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail,err: %s ", err)
	}
	if ok,err := authorityCheck(schema.SAuth.Write, commonName);!ok {
		return "", fmt.Errorf("write %s table fail,err: %s ", args[0],err)
	}
	if err := json.Unmarshal([]byte(args[1]), &PutDatas); err != nil {
		return "", fmt.Errorf("%s table parameters Unmarshal fail,put string not json array, err:%s",args[0], err)
	}
	for k, mapres := range PutDatas {
		Id, ok := mapres["id"]
		if !ok {
			return "", fmt.Errorf("The id field must exist ")
		}
		if !SchemaCheck(schema.Fields, mapres) {
			return "", fmt.Errorf("SchemaCheck fail,args index %d string doesn't match %s format", k, args[0])
		}
		if err := FiledsCheck(schema.Fields,mapres);err !=nil{
			return "",err
		}
		putDate, _ := json.Marshal(mapres)
		if err := stub.PutState(PREFIX+"_"+args[0]+"_"+Id.(string), putDate); err != nil {
			return "", fmt.Errorf("PutState fail, err :%s", err)
		}
	}
	return fmt.Sprintf("%s put data success!", args[0]), nil
}

/*
	修改一个表的一条数据的一个或者多个字段值
*/
func update(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("update data operation expected 2 parameters! ")
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	schema := &Schema{}
	schemaRes, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(schemaRes), &schema); err != nil {
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail,err: %s ", err)
	}
	var Updata map[string]interface{}
	var allMap map[string]interface{}
	if err := json.Unmarshal([]byte(args[1]), &Updata); err != nil {
		return "", fmt.Errorf("%s table parameters Unmarshal fail,err: %s",args[0], err)
	}
	Id, ok := Updata["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	if ok ,_ := authorityCheck(schema.SAuth.Write, commonName);!ok {
		if ok, err := parsauthorityCheck(schema.Fields, Updata, commonName); !ok {
			return "", fmt.Errorf("authority Check err: %s", err)
		}
	}
	if err := FiledsCheck(schema.Fields,Updata);err !=nil{
		return "",err
	}
	result, err := stub.GetState(PREFIX+"_"+args[0]+"_"+Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("GetState %s data fail,please check your parameters ",args[0]+"_"+Id.(string))
	}
	if err := json.Unmarshal(result, &allMap); err != nil {
		return "", fmt.Errorf("The original data Unmarshal fail err:%s ", err)
	}
	if err := Deassign(Updata, allMap); err != nil {
		return "", err
	}

	resultByte, _ := json.Marshal(allMap)
	if err := stub.PutState(PREFIX+"_"+args[0]+"_"+Id.(string), resultByte); err != nil {
		return "", fmt.Errorf("Put %s date fail, err :%s ",args[0]+"_"+Id.(string), err)
	}
	return fmt.Sprintf("%s update data success!", args[0]), nil

}

func get(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("get data operation expected  2 parameters to function get!")
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	var parameters map[string]interface{}
	var mapResult map[string]interface{}

	if err := json.Unmarshal([]byte(args[1]), &parameters); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,err: %s", err)
	}

	Id, ok := parameters["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(PREFIX+"_"+args[0]+"_"+Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("get %s data fail,please check your parameters ",args[0]+"_"+Id.(string))
	}
	schema := &Schema{}
	schemaRes, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	if err := json.Unmarshal([]byte(schemaRes), &schema); err != nil {
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail,err: %s ", err)
	}
	if err := json.Unmarshal(result, &mapResult); err != nil {
		return "", fmt.Errorf("The original data Unmarshal fail err:%s ", err)
	}
	if ok,_:= authorityCheck(schema.SAuth.Read, commonName);!ok {
		if filteredData, err := dataFilter(schema.Fields, mapResult, commonName); err != nil {
			return "", fmt.Errorf("get data authority filter fail, err: %s", err)
		} else {
			result, _ := json.Marshal(filteredData)
			return string(result), nil
		}

	} else {
		return string(result), nil
	}
}

/*
	重新赋值，并检查相应的格式是否正确
*/
func Deassign(upMap, result map[string]interface{}) error {
	if len(upMap) > len(result) {
		return fmt.Errorf("Deassign fail,reassigned data length greater than the original data length! ")
	}
	for k, v := range upMap {
		if resultV, ok := result[k]; ok {
			if resultMap, resultok := resultV.(map[string]interface{}); resultok {
				if vMap, ok := v.(map[string]interface{}); !ok {
					return fmt.Errorf("Deassign fail, format doesn't match! ")
				} else {
					Deassign(vMap, resultMap)
				}
			} else  {
				result[k] = v
			}
		} else {
			return fmt.Errorf("Deassign fail %s key nonentity ", k)
		}
	}
	return nil
}

func SchemaCheck(schema map[string]SchemaParameters, checkmap map[string]interface{}) bool {
	if len(schema) != len(checkmap) {
		return false
	}
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return false
		} else {
			if parameterS, ok := schemaV.Value.(map[string]interface{}); ok {
				if parameterC, ok := v.(map[string]interface{}); !ok {
					return false
				}else{
					if !BackUpCheck(parameterS,parameterC){
						return false
					}
				}
			}
		}
	}
	return true
}

func FiledsCheck(schema map[string]SchemaParameters, checkmap map[string]interface{}) error{
	for k, v := range checkmap {
		if k != "backup" && schema[k].FCheck!= nil&&schema[k].FCheck.FType != ""{
			if err := fieldCheckout(schema[k].FCheck,v);err!=nil{
				return fmt.Errorf("%s %s ",k,err)
			}
		}
	}
	return nil
}

func BackUpCheck(backUp map[string]interface{}, checkmap map[string]interface{}) bool {
	if len(backUp) != len(checkmap) {
		return false
	}
	for k, _ := range checkmap {
		if _, ok := backUp[k]; !ok {
			return false
		}
	}
	return true
}

func SchemaProcess(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) < 2 {
		return "", fmt.Errorf("Expect features with 2 or more parameters to play! ")
	}
	schema := &Schema{}
	switch args[0] {

	case "put":
		return schema.put(args[1:], stub)
	case "update":
		return schema.update(args[1:], stub)
	case "get":
		return schema.getSchema(args[1], stub)
	default:
		return "", fmt.Errorf(fmt.Sprintf("Unsupported schema function of %s", args[0]))

	}
}

/*
	put
	args 字段说明：
	1：args[0]代表我们具体操作的表，
    2：args[1:]后面的数据表示要记录到数据库的数据,每一个string 必须是json字符串，并且需要每一个json 字符串都必须包含id 字段

我们可以发现json字符串都被我们解析成了map[string]interface{}类型的map,其中key代表表模板的字段，interface可以是一个结构，我们当前将他看做我们表模板字段的初始值。

*/
func (this *Schema) put(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("Schema  put operation expected  2 parameters!")
	}
	var schema *Schema
	var err error
	if schema ,err = NewSchema(args[1]);err != nil{
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail,err: %s ", err)
	}else{
		if backupValue, ok := schema.Fields["backup"]; ok {
			if _,ok := backupValue.Value.(map[string]interface{}); !ok {
				return "", fmt.Errorf("backup parameters Unmarshal fail,it not json string,err: %s", err)
			}
		}
	}
	_, ok := schema.Fields["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	if value, _ := stub.GetState(PREFIX+"_"+args[0]); value != nil {
		return "", fmt.Errorf("the schema %s already exists and cannot be added", args[0])
	}
	putDate, _ := json.Marshal(schema) //处理重复字段
	if err := stub.PutState(PREFIX+"_"+args[0], putDate); err != nil {
		return "", fmt.Errorf("Schema PutState fail,err: %s ", err)
	}
	return fmt.Sprintf("%s schema put success!", args[0]), nil
}

/*
	if update Schema 那么我们没有办法比较旧有的一个模板，
     Schema 的 update,不支持部分修改，因为存在增加和删除某些字段的情况。
	TODO,增加版本管理
*/
func (this *Schema) update(args []string, stub shim.ChaincodeStubInterface) (string, error) {
	if len(args) != 2 {
		return "", fmt.Errorf("Schema update operation expected 2 parameters! ")
	}
	var schema *Schema
	var err error
	if schema ,err = NewSchema(args[1]);err != nil{
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail,err: %s ", err)
	}else{
		if backupValue, ok := schema.Fields["backup"]; ok {
			if _,ok := backupValue.Value.(map[string]interface{}); !ok {
				return "", fmt.Errorf("backup parameters Unmarshal fail,it not json string,err: %s", err)
			}
		}
	}
	_, ok := schema.Fields["id"]
	if !ok {
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(PREFIX+"_"+args[0])
	if err != nil || result == nil {
		return "", fmt.Errorf("%s schema data doesn't exist,please check your parameters ",args[0])
	}

	if err := json.Unmarshal([]byte(result), &schema); err != nil {
		return "", fmt.Errorf("The parameters Unmarshal to a Schema structural fail, err: %s ", err)
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	if ok,err := authorityCheck(schema.SchemaAuth.Write,commonName);!ok{
		return "", fmt.Errorf("update %s schema fail,err: %s ",args[0],err)
	}
	if err = stub.PutState(PREFIX+"_"+args[0], []byte(args[1]));err != nil{
		return "", fmt.Errorf("%s schema data PutState fail,please try again ",args[0])
	}
	return fmt.Sprintf("%s schema update success!", args[0]), err

}

func (this *Schema) get(args string, stub shim.ChaincodeStubInterface) (string, error) {
	result, err := stub.GetState(PREFIX+"_"+args)
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema %s data fail,please check your parameters:err : %s ",args,err)
	}
	return string(result), nil
}

func (this *Schema) getSchema(args string, stub shim.ChaincodeStubInterface)(string, error) {
	result, err := stub.GetState(PREFIX+"_"+args)
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema %s data fail,please check your parameters:err : %s ",args,err)
	}
	if err := json.Unmarshal(result, this); err != nil {
		return "", fmt.Errorf("SchemaData Unmarshal fail, err: %s", err)
	}
	commonName, err := getCertificateCommonName(stub)
	if err != nil {
		return "", fmt.Errorf("getCertificateCommonName happen err: %s ", err)
	}
	if ok,err := authorityCheck(this.SchemaAuth.Read,commonName);!ok{
		return "",fmt.Errorf("get %s schema fail,err: %s", args,err)
	}
	return string(result), nil
}

func getCertificateCommonName(stub shim.ChaincodeStubInterface) (string, error) {
	return "Admin@org2.example.com",nil
	creatorByte, _ := stub.GetCreator()
	certStart := bytes.IndexAny(creatorByte, "-----BEGIN")
	if certStart == -1 {
		return "", fmt.Errorf("No certificate found ")
	}
	certText := creatorByte[certStart:]
	bl, _ := pem.Decode(certText)
	if bl == nil {
		return "", fmt.Errorf("Could not decode the PEM structure ")
	}

	cert, err := x509.ParseCertificate(bl.Bytes)
	if err != nil {
		return "", fmt.Errorf("ParseCertificate failed")
	}
	return cert.Subject.CommonName, nil
}

/*
	检查一组参数的权限是否匹配
*/
func parsauthorityCheck(schema map[string]SchemaParameters, checkmap map[string]interface{}, commonName string) (bool, error) {
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return false, fmt.Errorf("Update data parameter mismatch with schema structure! ")
		} else {
			if _, ok := schemaV.Value.(map[string]interface{}); ok {
				if _, ok := v.(map[string]interface{}); !ok {
					return false, fmt.Errorf("Update data parameter mismatch with schema structure! ")
				}
			}
			//	if !authorityCheck(schemaV.Write, commonName) {
			//		return false, fmt.Errorf("%s field permission check does not match", k)
			//	}
			//	else {
			//		if res, err := parsauthorityCheck(parameterS, parameterC, commonName); !res {
			//			return res, err
			//		}
			//	}
			//} else {
				if ok,err := authorityCheck(schemaV.PAuth.Write, commonName);!ok {
					return false, fmt.Errorf("%s field permission check does not match, err: %s ", k,err)
				}
			//}
		}
	}
	return true, nil
}

/*
	根据权限拼装数据,进行数据过滤
*/
func dataFilter(schema map[string]SchemaParameters, checkmap map[string]interface{}, commonName string) (map[string]interface{}, error) {
	for k, v := range checkmap {
		if schemaV, ok := schema[k]; !ok {
			return nil, fmt.Errorf("Update data parameter mismatch with schema structure! ")
		} else {
			if _, ok := schemaV.Value.(map[string]SchemaParameters); ok {
				if _, ok := v.(map[string]interface{}); !ok {
					return nil, fmt.Errorf("Update data parameter mismatch with schema structure! ")
				}
				if ok,_ := authorityCheck(schemaV.PAuth.Read, commonName);!ok {
					checkmap[k] = defValue
				}
				//else {
				//	if _, err := dataFilter(parameterS, parameterC, commonName); err!=nil {
				//		return nil, err
				//	}
				//}
			} else {
				if ok,_ := authorityCheck(schemaV.PAuth.Read, commonName);!ok {
					checkmap[k] = defValue
				}
			}
		}
	}
	return checkmap, nil
}

func authorityCheck(authority AuthGroup, commonName string) (bool,error) {
	var auths map[string]int64
	if IsUsers {
		auths = authority.Users
	}else{
		auths = authority.Roles
	}
	if len(auths) == 0 {
		return true,nil
	}
	for k, v := range auths {
		if k == commonName {
			if v == 0 || v > time.Now().Unix(){
				return true,nil
			}else{
				return false,fmt.Errorf("%s user permission period ",commonName)
			}
		}
	}
	return false,fmt.Errorf("%s user does not have permission",commonName)

}

func NewSchema(arg string)(*Schema,error){
	 schema := &Schema{
	 	Fields:make(map[string]SchemaParameters),
	 }
	var argMap map[string]interface{}
	if err := json.Unmarshal([]byte(arg),&argMap);err!=nil{
		return nil,err
	}
	if argMap["fields"] != nil && argMap["schema_auth"] != nil && argMap["auth"] != nil{
		if err := json.Unmarshal([]byte(arg),schema);err!=nil{
			return nil,err
		}
		return schema,nil
	}
	for k,v := range argMap{
		schema.Fields[k] = SchemaParameters{
			Value:v,
			PAuth:Auth{},
		}
	}
	return schema,nil
}

func fieldCheckout(check *Checks,val interface{})error{
	fType := reflect.TypeOf(val).String()

	switch check.FType {
	case "string":
		if fType != "string"{
			return fmt.Errorf("fields type does not match the defined string type! ")
		}
		if check.Must && (val == ""){
			return fmt.Errorf("fields can not be empty")
		}
		value := val.(string)
		if len([]rune(value))>check.Fieldlen{
			return fmt.Errorf("fields length must be greater than %d ",check.Fieldlen)
		}
	case "int","int16","int32","int64","float32","float64":
		if fType != "float64"{
			return fmt.Errorf("fields type does not match the defined %s type! ",check.FType )
		}
		value := val.(float64)
		if check.Must && (value == 0){
			return fmt.Errorf("fields can not be 0")
		}
		if check.Fieldlen !=0  && value < 0{
			return fmt.Errorf("fields cannot be negative")
		}
	case "time.Time":
		if fType != "string"{
			return fmt.Errorf("fields type does not match the defined time.Time type! ")
		}
		dateStr := reflect.ValueOf(val).String()
		if check.Must && (dateStr == "0001-01-01 00:00:00 +0000 UTC"|| dateStr == "0001-01-01T00:00:00Z"){
			return fmt.Errorf("fields cannot be initial value")
		}
	default:
		fmt.Errorf("fields type no match! ")
	}
	return nil
}

func InvokeTable(args []string,stub shim.ChaincodeStubInterface) pb.Response{
	var tets map[string]interface{}
	err := json.Unmarshal([]byte(args[1]),&tets)
	if err != nil{
		return 	shim.Error(err.Error())
	}
	err = tableDifferentiate(args[0],tets,stub)
	if err!=nil {
	return 	shim.Error(err.Error())
	}
	return shim.Success([]byte("successful"))
}
/*
	根据参数区分不同的表，并执行不同的操作
*/
func tableDifferentiate(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)(error){
	switch tableName {

	case "alibusi":
		return alibusiLeadger(tableName,value, stub)
	case "alibusirecord":
		return addAlibusirecord(tableName,value, stub)
	case "merchants":
		return merchantsLeadger(tableName,value, stub)
	case "couincomingparts":
		//return addMerchantsale(args[1:],stub)
	case "merchantsale":
		return addMerchantsale(tableName,value, stub)
	case "coutickets":
		return couticketsLeadger(tableName,value, stub)
	case "coutcantm":
		return coutcantmLeadger(tableName,value, stub)
	case "couticketcodes":
		//return coupartnersLeadger(args[1:],stub)
	case "coupartners":
		return coupartnersLeadger(tableName,value, stub)
	case "couticketorders":
		return addCouticketorders(tableName,value,stub)
	default:
		return fmt.Errorf(fmt.Sprintf("Unsupported table name of %s", tableName))

	}
	return nil
}

/*
	当增加Alibusi（机构）数据时，这里我们不支持覆盖.如果机构账本数据不存在我们新建机构账本数据。
		当前账本数据只包含剩余保证金，以及后续预付金的增加。
*/
func alibusiLeadger(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)(error){
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_alibusiLeadger_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ",tableName+Id)
	}
	abail,ok := value["abail"].(float64)
	if !ok {
		return fmt.Errorf("amount fields type not number! ")
	}
	amount,ok := value["amount"].(float64)
	if !ok {
		return fmt.Errorf("amount fields type not number! ")
	}
	alibusi := AlibusiLeadger{
		Id:Id,
		Abail:abail,
		Amount:amount,
	}
	alibyte , _:= json.Marshal(alibusi)
	if err := stub.PutState(PREFIX+"_alibusiLeadger_"+ Id,alibyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ",tableName+Id,err)
	}
	return nil
}
/*
	机构资金表变动，会直接作用于对应的机构账单表
*/
func addAlibusirecord(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id,ok := value["aid"].(string)
	if !ok {
		return fmt.Errorf("aid fields type not string! ")
	}
	result,err := stub.GetState(PREFIX+"_alibusiLeadger_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result ==nil{
		return fmt.Errorf("%s leadger data not exist! ",tableName+Id)
	}
	alibusi := &AlibusiLeadger{}
	if err := json.Unmarshal(result,alibusi);err != nil{
		return fmt.Errorf("json Unmarshal fail,err: %s",err)
	}
	action,ok := value["action"].(float64)
	if !ok {
		return fmt.Errorf("action fields type not number! ")
	}
	amount,ok := value["amount"].(float64)
	if !ok {
		return fmt.Errorf("amount fields type not number! ")
	}
	samount,ok := value["samount"].(float64)
	if !ok {
		return fmt.Errorf("samount fields type not number! ")
	}
	if action==1{
		alibusi.Amount -= amount

	}else{
		alibusi.Amount += amount
	}
	if alibusi.Amount != samount{
		fmt.Println("alibus amount different from alibusirecord table samount !")
	}
	alibyte , _:= json.Marshal(alibusi)
	if err := stub.PutState(PREFIX+"_alibusiLeadger_"+ Id,alibyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ",tableName+Id,err)
	}
	return nil
}

/*
	商户账本表，当增加一张商户表时我们需要增加一张商户账本表，主要记录商户预付金的变化
*/
func merchantsLeadger(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_merchantsLeadger_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ",tableName+Id)
	}
	regular ,ok := value["regular"].(float64)
	if !ok {
		fmt.Errorf("regular fields type not number! ")
	}
	Bid ,ok := value["bid"].(string)
	if !ok {
		fmt.Errorf("bid fields type not string! ")
	}
	rregular ,ok := value["rregular"].(float64)
	if !ok {
		fmt.Errorf("rregular fields type not number! ")
	}
	merchantsL := MerchantsLeadger{
		Id:Id,
		Bid:Bid,
		Regular:regular,
		Rregular:rregular,
	}
	merbyte , _:= json.Marshal(merchantsL)
	if err := stub.PutState(PREFIX+"_merchantsLeadger_"+ Id,merbyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ",tableName+Id,err)
	}
	return nil
}

/*
	当增加一条交易记录的时候，我们需要根据交易记录实现，1.预付金的计算，2.计算分成，3.预付金的变化。分成的计算
*/
func addMerchantsale(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_"+tableName+"_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s data already exist! ",tableName+Id)
	}
	mer := &Merchantsale{}
	valByte,_ := json.Marshal(value)
	err = json.Unmarshal(valByte,mer)
	if err != nil {
		return fmt.Errorf("Merchantsale struct json unmarshal fail,err : %s ",err)
	}
	//1.计算预付金 这部分待定
	var treposit float64
	if mer.Nodisamount == 0 || mer.Disamount == 0{ //如果打折金额与不打折金额任一为0，则认为全部打折
		treposit = Decimal(mer.Oamount * mer.Adiscounts)
	}else{
		treposit = Decimal(mer.Nodisamount+ mer.Disamount * mer.Adiscounts)
	}
	if treposit != mer.Treposit {
		return fmt.Errorf("treposit calculation fail ")
	}
	if mer.Fee != Decimal(mer.Tamount * mer.Feerate){
		return fmt.Errorf("fee calculation fail ")
	}
	profit := mer.Tamount - mer.Fee - treposit //计算利润
	if profit < 0 {
		return fmt.Errorf("profit is negative ")
	}
	//减少商户剩余预付金
	merL := &MerchantsLeadger{}
	resMer,err := stub.GetState(PREFIX+"_merchantsLeadger_"+ mer.Mid)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ","_merchantsLeadger_"+Id,err)
	}
	if resMer == nil{
		return fmt.Errorf("%s leadger data not exist! ","_merchantsLeadger_"+Id)
	}
	err = json.Unmarshal(resMer,merL)
	if err != nil{
		return fmt.Errorf("MerchantsLeadger json unmarshal err :%s",err)
	}
	merL.Regular -= treposit
	if mer.Afreposit == merL.Regular {
		merbyte , _:= json.Marshal(merL)
		if err := stub.PutState(PREFIX+"_merchantsLeadger_"+ Id,merbyte);err!=nil{
			return fmt.Errorf("%s leadger data putstate fail: %s ","_merchantsLeadger_"+Id,err)
		}
	}
	// 增加机构可提取资金
	aliL := &AlibusiLeadger{}
	resAli,err := stub.GetState(PREFIX+"_alibusiLeadger_"+ merL.Bid)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ","_alibusiLeadger_"+merL.Bid,err)
	}
	if resAli == nil{
		return fmt.Errorf("%s leadger data not exist! ","_alibusiLeadger_"+merL.Bid)
	}
	err = json.Unmarshal(resAli,aliL)
	if err != nil{
		return fmt.Errorf("AlibusiLeadger json unmarshal err :%s",err)
	}
	aliL.Amount += treposit
	alibyte , _:= json.Marshal(aliL)
	if err := stub.PutState(PREFIX+"_alibusiLeadger_"+ merL.Bid,alibyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ","_alibusiLeadger_"+merL.Bid,err)
	}
	//新建分成数据
	err = addProfit(Id,0,profit,stub)
	if err!= nil{
		return fmt.Errorf("add profit data fail,err: %s",err)
	}
	return nil
}

/*
	当增加一张新券表的时候，我们同时增长一张券码库存表，用来记录，码库存的变化。
*/
func couticketsLeadger(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_couticketsLeadger_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ",tableName+Id)
	}
	codenum,ok := value["codenum"].(float64)
	if !ok {
		fmt.Errorf("codenum fields type not number! ")
	}
	codestork,ok := value["codestork"].(float64)
	if !ok {
		fmt.Errorf("codestork fields type not number! ")
	}
	couticketeL := CouticketsLeadger{
		Id:Id,
		Codenum:int(codenum),
		Codestork:int(codestork),
	}
	merbyte , _:= json.Marshal(couticketeL)
	if err := stub.PutState(PREFIX+"_couticketsLeadger_"+ Id,merbyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ",tableName+Id,err)
	}
	return nil
}

/*
	当增加券可用门店时，我们同样需要增加一张券码可用门店库存表
*/
func coutcantmLeadger(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	MId := value["id"].(string)
	TId := value["tid"].(string)
	result,err := stub.GetState(PREFIX+"_coutcantmLeadger_"+TId+"_"+MId)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+TId+MId,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ",tableName+TId+MId)
	}
	codenum,ok := value["codenum"].(float64)
	if !ok {
		fmt.Errorf("codenum fields type not number! ")
	}
	resid,ok := value["resid"].(float64)
	if !ok {
		fmt.Errorf("resid fields type not number! ")
	}
	coutcantmL := CoutcantmLeadger {
		Id:MId,
		Tid:TId,
		Codenum:int(codenum),
		Resid:int(resid),
	}
	conbyte , _:= json.Marshal(coutcantmL)
	if err := stub.PutState(PREFIX+"_coutcantmLeadger_"+TId+"_"+MId,conbyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ",tableName+TId+MId,err)
	}
	return nil
}
/*
	当我们增加一个合作方信息表时，我们同时需要增加一张分润记录表，记录合作方的分润情况
*/
func coupartnersLeadger(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_coupartnersLeadger_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ",tableName+Id)
	}
	//benefittype,ok := value["benefittype"].(float64)
	//if !ok {
	//	fmt.Errorf("benefittype fields type not number! ")
	//}
	//benefit,ok := value["benefit"].(float64)
	//if !ok {
	//	fmt.Errorf("benefit fields type not number! ")
	//}//缺少分润金额


	return nil
}

/*
	增加一张订单表时，我们要做相应的工作1.检查金额。2.计算分成 3.执行码变化
*/
func addCouticketorders(tableName string,value map[string]interface{}, stub shim.ChaincodeStubInterface)error{
	Id := value["id"].(string)
	result,err := stub.GetState(PREFIX+"_"+tableName+"_"+ Id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ",tableName+Id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s data already exist! ",tableName+Id)
	}
	cout := &Couticketorders{}
	valByte,_ := json.Marshal(value)
	err = json.Unmarshal(valByte,cout)
	if err != nil {
		return fmt.Errorf("Couticketorders struct json unmarshal fail,err : %s ",err)
	}
	//计算
	return nil
}


func addProfit(id string,typ int,profit float64,stub shim.ChaincodeStubInterface)error{
	result,err := stub.GetState(PREFIX+"_profit_"+ id)
	if err != nil {
		return fmt.Errorf("%s GetState data fail: err %s! ","profit_"+ id,err)
	}
	if result !=nil{
		return fmt.Errorf("%s leadger data already exist! ","profit_"+ id)
	}
	prof := &Profit{
		Id:id,
		Type:typ,
		profit:profit,
	}
	profbyte , _:= json.Marshal(prof)
	if err := stub.PutState(PREFIX+"_profit_"+id,profbyte);err!=nil{
		return fmt.Errorf("%s leadger data putstate fail: %s ","profit_"+ id,err)
	}
	return nil
}

func Decimal(value float64) float64 {
	value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64)
	return value
}

func main() {
	err := shim.Start(&GXHCC{})
	if err != nil {
		fmt.Printf("Error starting EncCC chaincode: %s", err)
	}
}
