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"
)


type GXHCC struct {
}


type Schema struct {
}


// 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 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 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 function err: %s", err))
		}
		return shim.Success([]byte(res))
	case "cert":
		testCertificate(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("Expected  more than 2 parameters to function put!")
	}
	var Result []map[string]interface{}
	var schemaMap map[string]interface{}
	schema := &Schema{}
	result, err := schema.get(args[0], stub)
	if err != nil {
		return "", err
	}
	json.Unmarshal([]byte(result),&schemaMap)

	if err := json.Unmarshal([]byte(args[1]), &Result); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json array, err:%s", err)
	}
	for k, mapres := range Result{
		Id ,ok:= mapres["id"]
		if !ok{
			return "", fmt.Errorf("The id field must exist ")
		}
		//if result,err := stub.GetState(args[0]+Id.(string));err!=nil{
		//	return "", fmt.Errorf("get %s data happen err: ", args[0]+Id.(string))
		//}else if result!=nil{
		//	return "", fmt.Errorf("%s data already exist", args[0]+Id.(string))
		//}
		if !SchemaCheck(schemaMap,mapres){
			return "", fmt.Errorf("SchemaCheck fail,args index %d string doesn't match %s format", k,args[0])
		}
		putDate,_:=json.Marshal(mapres)
		if err := stub.PutState(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("Expected 2 parameters to function update! ")
	}
	var mapResult map[string]interface{}
	var allMap map[string]interface{}
	if err := json.Unmarshal([]byte(args[1]), &mapResult); err != nil  {
		return "", fmt.Errorf("parameters Unmarshal fail,args  string not json string,err: %s", err)
	}

	Id ,ok:= mapResult["id"]
	if !ok{
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(args[0] + Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("GetState data fail,please check your parameters ")
	}
	if err := json.Unmarshal(result, &allMap); err != nil {
		return "", fmt.Errorf("result parameters Unmarshal fail err:%s ", err)
	}
	if err := Deassign(mapResult, allMap); err != nil {
		return "", err
	} else {
		resultByte, _:= json.Marshal(allMap)
		if err := stub.PutState(args[0]+Id.(string), resultByte);err!=nil{
			return "", fmt.Errorf("updateState date fail, err :%s ", 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("Expected  2 parameters to function get!")
	}
	var mapResult map[string]interface{}
	if err := json.Unmarshal([]byte(args[1]), &mapResult); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	}

	Id := mapResult["id"]
	Id ,ok:= mapResult["id"]
	if !ok{
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(args[0] + Id.(string))
	if err != nil || result == nil {
		return "", fmt.Errorf("GetState data fail,please check your parameters ")
	}
	return string(result), nil
}

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


func SchemaCheck(schema, 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.(map[string]interface{}); ok {
				if parameterC, ok := v.(map[string]interface{}); !ok {
					return false
				} else {
					if !SchemaCheck(parameterS, parameterC) {
						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.get(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("Expected  2 parameters to function put!")
	}
	var mapResult map[string]interface{}

	if err := json.Unmarshal([]byte(args[1]), &mapResult); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	}else{
		if extrastr,ok := mapResult["extra"];ok{
			var extraResult map[string]interface{}
			if err := json.Unmarshal([]byte(extrastr.(string)), &extraResult); err != nil {
				return "", fmt.Errorf("parameters Unmarshal fail,extra string not json string,err: %s", err)
			}
		}
	}
	_ ,ok:= mapResult["id"]
	if !ok{
		return "", fmt.Errorf("The id field must exist ")
	}
	if value,_ :=stub.GetState(args[0]);value != nil{
		return "", fmt.Errorf("the parameter %s already exists and cannot be added", args[0])
	}
	putDate,_:= json.Marshal(mapResult) //处理重复字段
	if err := stub.PutState(args[0], putDate); err != nil {
		return "", fmt.Errorf("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("expected 2 parameters to function put!")
	}
	var mapResult map[string]interface{}

	if err := json.Unmarshal([]byte(args[1]), &mapResult); err != nil {
		return "", fmt.Errorf("parameters Unmarshal fail,args string not json string,err: %s", err)
	}else{
		if extrastr,ok := mapResult["extra"];ok{
			var extraResult map[string]interface{}
			if err := json.Unmarshal([]byte(extrastr.(string)), &extraResult); err != nil {
				return "", fmt.Errorf("parameters Unmarshal fail,extra string not json string,err: %s", err)
			}
		}
	}
	_ ,ok:= mapResult["id"]
	if !ok{
		return "", fmt.Errorf("The id field must exist ")
	}
	result, err := stub.GetState(args[0])
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema data fail,please check your parameters ")
	}

	err = stub.PutState(args[0], []byte(args[1]))
	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(args)
	if err != nil || result == nil {
		return "", fmt.Errorf("GetSchema data fail,please check your parameters ")
	}
	return string(result), nil
}

func  testCertificate(stub shim.ChaincodeStubInterface) pb.Response{
	creatorByte,_:= stub.GetCreator()
	certStart := bytes.IndexAny(creatorByte, "-----BEGIN")
	if certStart == -1 {
		fmt.Errorf("No certificate found")
	}
	certText := creatorByte[certStart:]
	bl, _ := pem.Decode(certText)
	if bl == nil {
		fmt.Errorf("Could not decode the PEM structure")
	}

	cert, err := x509.ParseCertificate(bl.Bytes)
	if err != nil {
		fmt.Errorf("ParseCertificate failed")
	}
	fmt.Println("cert  ",cert)
	result,err := json.Marshal(cert)
	if err!=nil{
		fmt.Println("Marshal err:",err)
	}
	fmt.Println("cert marshal:",string(result))
	fmt.Println("cert Subject ",cert.Subject)
	uname:=cert.Subject.CommonName
	fmt.Println("Name:"+uname)

	return shim.Success([]byte("Called testCertificate "+uname))
}


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



