package common

import (
	"bytes"
	"encoding/binary"
	"io"
	"math"
	"mime/multipart"
)

import (
	"errors"
)

var (
	versions = []string{"2.5", "x", "2", "1"}
	layers   = []string{"x", "3", "2", "1"}
	bitRates = map[string][]int{
		"V1Lx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		"V1L1": []int{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448},
		"V1L2": []int{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
		"V1L3": []int{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320},
		"V2Lx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		"V2L1": []int{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256},
		"V2L2": []int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
		"V2L3": []int{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
		"VxLx": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		"VxL1": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		"VxL2": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		"VxL3": []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	}
	sampleRates = map[string][]int{
		"x":   []int{0, 0, 0},
		"1":   []int{44100, 48000, 32000},
		"2":   []int{22050, 24000, 16000},
		"2.5": []int{11025, 12000, 8000},
	}
	samples = map[string]map[string]int{
		"x": {
			"x": 0,
			"1": 0,
			"2": 0,
			"3": 0,
		},
		"1": { //MPEGv1,     Layers 1,2,3
			"x": 0,
			"1": 384,
			"2": 1152,
			"3": 1152,
		},
		"2": { //MPEGv2/2.5, Layers 1,2,3
			"x": 0,
			"1": 384,
			"2": 1152,
			"3": 576,
		},
	}
)

type frame struct {
	bitRate    int
	sampleRate int
	frameSize  int
	sample     int
}

func skipID3(buffer []byte) int {
	var id3v2Flags, z0, z1, z2, z3 byte
	var tagSize, footerSize int

	//http://id3.org/d3v2.3.0
	if buffer[0] == 0x49 && buffer[1] == 0x44 && buffer[2] == 0x33 { //'ID3'
		id3v2Flags = buffer[5]
		if (id3v2Flags & 0x10) != 0 {
			footerSize = 10
		} else {
			footerSize = 0
		}

		//ID3 size encoding is crazy (7 bits in each of 4 bytes)
		z0 = buffer[6]
		z1 = buffer[7]
		z2 = buffer[8]
		z3 = buffer[9]
		if ((z0 & 0x80) == 0) && ((z1 & 0x80) == 0) && ((z2 & 0x80) == 0) && ((z3 & 0x80) == 0) {
			tagSize = (((int)(z0 & 0x7f)) * 2097152) +
				(((int)(z1 & 0x7f)) * 16384) +
				(((int)(z2 & 0x7f)) * 128) +
				((int)(z3 & 0x7f))
			return 10 + tagSize + footerSize
		}
	}
	return 0
}

func frameSize(samples int, layer string, bitRate, sampleRate, paddingBit int) int {
	if sampleRate == 0 {
		return 0
	} else if layer == "1" {
		return ((samples * bitRate * 125 / sampleRate) + paddingBit*4)
	} else { //layer 2, 3
		return (((samples * bitRate * 125) / sampleRate) + paddingBit)
	}
}

func parseFrameHeader(header []byte) *frame {
	b1 := header[1]
	b2 := header[2]

	versionBits := (b1 & 0x18) >> 3
	version := versions[versionBits]

	var simpleVersion string
	if version == "2.5" {
		simpleVersion = "2"
	} else {
		simpleVersion = version
	}

	layerBits := (b1 & 0x06) >> 1
	layer := layers[layerBits]

	bitRateKey := "V" + simpleVersion + "L" + layer
	bitRateIndex := (b2 & 0xf0) >> 4

	var bitRate int
	frameBitRates, exists := bitRates[bitRateKey]
	if exists && int(bitRateIndex) < len(frameBitRates) {
		bitRate = frameBitRates[bitRateIndex]
	} else {
		bitRate = 0
	}

	sampleRateIdx := (b2 & 0x0c) >> 2
	var sampleRate int
	frameSampleRates, exists := sampleRates[version]
	if exists && int(sampleRateIdx) < len(frameSampleRates) {
		sampleRate = frameSampleRates[sampleRateIdx]
	} else {
		sampleRate = 0
	}
	sample := samples[simpleVersion][layer]

	paddingBit := (b2 & 0x02) >> 1
	return &frame{
		bitRate:    bitRate,
		sampleRate: sampleRate,
		frameSize:  frameSize(sample, layer, bitRate, sampleRate, int(paddingBit)),
		sample:     sample,
	}
}

func round(duration float64) float64 {
	return math.Round(duration*1000) / 1000 //round to nearest ms
}

// BoxHeader 信息头
type BoxHeader2 struct {
	Size       uint32
	FourccType [4]byte
	Size64     uint64
}

func getHeaderBoxInfo2(data []byte) (boxHeader BoxHeader2) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &boxHeader)
	return
}

// Calculate returns the duration of an mp3 file
func GetMP3PlayDuration(file multipart.File) (duration float64, err error) {
	var size int64
	var info = make([]byte, 0x10)
	var boxHeader BoxHeader2
	file.ReadAt(info, 0)
	boxHeader = getHeaderBoxInfo2(info)
	size = int64(boxHeader.Size64)
	buffer := make([]byte, 100)
	if err != nil {
		return
	}
	var bytesRead int
	bytesRead, err = file.Read(buffer)
	if err != nil {
		return
	}
	if bytesRead < 100 {
		err = errors.New("Corrupt file")
		return
	}
	offset := int64(skipID3(buffer))
	buffer = make([]byte, 10)
	for offset < size {
		bytesRead, e := file.ReadAt(buffer, offset)
		if e != nil && e != io.EOF {
			err = e
			return
		}
		if bytesRead < 10 {
			duration = round(duration)
			return
		}

		if buffer[0] == 0xff && (buffer[1]&0xe0) == 0xe0 {
			info := parseFrameHeader(buffer)
			if info.frameSize > 0 && info.sample > 0 {
				offset += int64(info.frameSize)
				duration += (float64(info.sample) / float64(info.sampleRate))
			} else {
				offset++ //Corrupt file?
			}
		} else if buffer[0] == 0x54 && buffer[1] == 0x41 && buffer[2] == 0x47 { //'TAG'
			offset += 128 //Skip over id3v1 tag size
		} else {
			offset++ //Corrupt file?
		}
	}
	duration = round(duration)
	return
}
