package icsmediaconv

import (
	"fmt"
	"sync"

	"gitlab.com/ics_cinnamon/joy4/av"
	"gitlab.com/ics_cinnamon/joy4/cgo/ffmpeg"
	"gitlab.com/ics_cinnamon/joy4/codec"
	"gitlab.com/ics_cinnamon/voicegateway/icserror"
	"gitlab.com/ics_cinnamon/voicegateway/icslog"
	"gitlab.com/ics_cinnamon/voicegateway/icspacketparser"
)

const (
	ICS_PT_MULAW = 0
	ICS_PT_ALAW  = 8
	ICS_PT_G729  = 18
	ICS_PT_END   = ICS_PT_G729
)

const PCM_8K_16BIT_10MS_SIZE = 160

type Converter struct {
	payloadtype icspacketparser.PayloadType
	codec       av.AudioCodecData
	name        string
	decoder     *ffmpeg.AudioDecoder
	encoder     *ffmpeg.AudioEncoder

	samplingRate  int
	onePacketSize int

	//frame *av.AudioFrame

	isStart bool
	m       *sync.Mutex

	ID int
}

func NewConverter(id int, pt icspacketparser.PayloadType, dec bool) (*Converter, *icserror.IcsError) {
	conv := &Converter{payloadtype: pt}

	conv.ID = id
	conv.isStart = false
	conv.m = &sync.Mutex{}

	if dec { //decoding
		switch pt {
		case ICS_PT_MULAW:
			conv.codec = codec.NewPCMMulawCodecData()
			conv.samplingRate = 8000
			conv.onePacketSize = 80
			//conv.onePacketSize = 160
		case ICS_PT_ALAW:
			conv.codec = codec.NewPCMAlawCodecData()
			conv.samplingRate = 8000
			conv.onePacketSize = 80
			//conv.onePacketSize = 160
		case ICS_PT_G729:
			conv.codec = codec.NewG729CodecData()
			conv.samplingRate = 8000
			conv.onePacketSize = 10
		default:
			return nil, icserror.ICSERRCONVNotSupportedCodec
		}

		var err error
		conv.decoder, err = ffmpeg.NewAudioDecoder(conv.codec)
		if err != nil {
			icserror.ICSERRCONVNotSupportedCodec.SetError(err)
			return nil, icserror.ICSERRCONVNotSupportedCodec
		}
	} else { //encoding
		switch pt {
		case ICS_PT_MULAW:
			conv.codec = codec.NewPCMMulawCodecData()
			conv.name = "mulaw"
			conv.samplingRate = 8000
			conv.onePacketSize = 160
		case ICS_PT_ALAW:
			conv.codec = codec.NewPCMAlawCodecData()
			conv.name = "alaw"
			conv.samplingRate = 8000
			conv.onePacketSize = 160
		case ICS_PT_G729:
			conv.codec = codec.NewG729CodecData()
			conv.name = "g729"
			conv.samplingRate = 8000
			conv.onePacketSize = 10
		default:
			return nil, icserror.ICSERRCONVNotSupportedCodec
		}

		var err error
		conv.encoder, err = ffmpeg.NewAudioEncoderByCodecType(conv.codec.Type())
		if err != nil {
			fmt.Println("########111", err)
			icserror.ICSERRCONVNotSupportedCodec.SetError(err)
			return nil, icserror.ICSERRCONVNotSupportedCodec
		}
		conv.encoder.SetChannelLayout(av.CH_MONO)
		conv.encoder.SetSampleRate(conv.samplingRate)
		conv.encoder.SetSampleFormat(av.S16)
		// fmt.Println("########222", conv.name)
	}
	//ffmpeg.SetLogLevel(ffmpeg.QUIET)

	conv.Start()

	return conv, nil
}

func (c *Converter) Start() {
	c.m.Lock()
	c.isStart = true
	c.m.Unlock()
}

func (c *Converter) Stop() {
	c.m.Lock()
	c.isStart = false
	c.m.Unlock()
}

func (c *Converter) IsStart() bool {
	return c.isStart
}

func (c *Converter) Close() {
	c.Stop()
	if c.decoder != nil {
		c.decoder.Close()
	}
	if c.encoder != nil {
		c.encoder.Close()
	}

	l := icslog.GetIcsLog()
	l.Print(icslog.LOG_LEVEL_INFO, c.ID, "Closed Converter")
}

func (c *Converter) Decode(packet []byte) ([]byte, *icserror.IcsError) {
	if c == nil {
		return nil, icserror.ICSERRInvalidParam
	}
	//l := icslog.GetIcsLog()
	//l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "converter### Decode() packet length: %d", len(packet))

	retBuf := make([]byte, PCM_8K_16BIT_10MS_SIZE*2)
	packetLen := len(packet)
	iter := 0
	for packetLen >= c.onePacketSize {
		packetLen -= c.onePacketSize

		//fmt.Printf("### Decode() iter(%d) packetlen(%d)\n", iter, packetLen)
		buf := packet[c.onePacketSize*iter : c.onePacketSize*(iter+1)]
		//fmt.Printf("### Decode() iter(%d), buf length %d %v\n", iter, len(buf), buf)
		//l.Printf(icslog.LOG_LEVEL_DEBUG2, c.ID, "### Decode() iter(%d), buf length %d %v", iter, len(buf), buf)

		c.m.Lock()
		if c.IsStart() {
			ok, frame, errDec := c.decoder.Decode(buf)
			if !ok {
				icserror.ICSERRCONVDecodeFail.SetError(errDec)
				//icserror.ICSERRCONVDecodeFail.Print()
				c.m.Unlock()
				return nil, icserror.ICSERRCONVDecodeFail
			}

			//fmt.Println("###frame len", iter, PCM_8K_16BIT_10MS_SIZE*iter, PCM_8K_16BIT_10MS_SIZE*(iter+1))
			//fmt.Println("###frame len", len(frame.Data[0]), len(frame.Data), frame)
			copy(retBuf[PCM_8K_16BIT_10MS_SIZE*iter:PCM_8K_16BIT_10MS_SIZE*(iter+1)], frame.Data[0][:PCM_8K_16BIT_10MS_SIZE])
		}
		c.m.Unlock()

		/*
			f1, err := os.OpenFile("./tx.voice.raw", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
			if err != nil {
				log.Fatal(err)
			}
			f1.Write(frame.Data[0][:PCM_8K_16BIT_10MS_SIZE])
			f1.Sync()
			f1.Close()
		*/

		iter++
	}
	//fmt.Println("###retBuf len", len(retBuf), retBuf)

	return retBuf, nil
}

func (c *Converter) Encode(packet []byte) ([]byte, *icserror.IcsError) {
	if c == nil {
		return nil, icserror.ICSERRInvalidParam
	}
	//l := icslog.GetIcsLog()
	//l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "converter### Decode() packet length: %d", len(packet))

	retBuf := make([]byte, PCM_8K_16BIT_10MS_SIZE)
	//retBuf := make([]byte, PCM_8K_16BIT_10MS_SIZE*2)
	packetLen := len(packet)
	iter := 0
	var onePacketSize int = c.onePacketSize
	if c.codec.Type() == av.PCM_ALAW || c.codec.Type() == av.PCM_MULAW {
		onePacketSize = onePacketSize
		//onePacketSize = onePacketSize / 2
	}

	for packetLen >= onePacketSize {
		packetLen -= onePacketSize
		//for packetLen >= c.onePacketSize {
		//packetLen -= c.onePacketSize

		//fmt.Printf("### Decode() iter(%d) packetlen(%d)\n", iter, packetLen)

		buf := packet[onePacketSize*iter : onePacketSize*(iter+1)]
		//buf := packet[c.onePacketSize*iter : c.onePacketSize*(iter+1)]

		//fmt.Printf("### Decode() iter(%d), buf length %d %v\n", iter, len(buf), buf)
		//l.Printf(icslog.LOG_LEVEL_DEBUG2, c.ID, "### Decode() iter(%d), buf length %d %v", iter, len(buf), buf)

		tbuf := make([][]byte, 1)
		tbuf[0] = buf

		frame := av.AudioFrame{
			SampleFormat:  av.S16,
			ChannelLayout: av.CH_MONO,
			SampleCount:   c.onePacketSize / 2,
			SampleRate:    c.samplingRate,
			Data:          tbuf,
		}

		//fmt.Printf(">>>%+v\n", frame)

		c.m.Lock()
		if c.IsStart() {
			pcmFrame, errEnc := c.encoder.Encode(frame)
			//ok, frame, errDec := c.decoder.Decode(buf)
			if errEnc != nil {
				icserror.ICSERRCONVEncodeFail.SetError(errEnc)
				//icserror.ICSERRCONVDecodeFail.Print()
				c.m.Unlock()
				return nil, icserror.ICSERRCONVEncodeFail
			}

			//fmt.Println("###frame len", iter, PCM_8K_16BIT_10MS_SIZE*iter, PCM_8K_16BIT_10MS_SIZE*(iter+1))
			//fmt.Println("###frame len", len(frame.Data[0]), len(frame.Data), frame)
			copy(retBuf[(PCM_8K_16BIT_10MS_SIZE/2)*iter:(PCM_8K_16BIT_10MS_SIZE/2)*(iter+1)], pcmFrame[0][:(PCM_8K_16BIT_10MS_SIZE/2)])
			//copy(retBuf[PCM_8K_16BIT_10MS_SIZE*iter:PCM_8K_16BIT_10MS_SIZE*(iter+1)], pcmFrame[0][:PCM_8K_16BIT_10MS_SIZE])
			//fmt.Printf("%d$$$%v", len(pcmFrame[0]), pcmFrame[0])
		}
		c.m.Unlock()

		iter++
	}
	//fmt.Println("###retBuf len", len(retBuf), retBuf)

	return retBuf, nil
}