/*
 * MIT License
 *
 * Copyright (c) 2016 xiongziliang <771730766@qq.com>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifdef ENABLE_MP4V2

#include <sys/stat.h>
#include "Common/config.h"
#include "Mp4Maker.h"
#include "MediaRecorder.h"
#include "Util/File.h"
#include "Util/mini.h"
#include "Util/util.h"
#include "Util/NoticeCenter.h"
using namespace toolkit;

namespace mediakit {

string timeStr(const char *fmt) {
	std::tm tm_snapshot;
	auto time = ::time(NULL);
#if defined(_WIN32)
	localtime_s(&tm_snapshot, &time); // thread-safe
#else
	localtime_r(&time, &tm_snapshot); // POSIX
#endif
	const size_t size = 1024;
	char buffer[size];
	auto success = std::strftime(buffer, size, fmt, &tm_snapshot);
	if (0 == success)
		return string(fmt);
	return buffer;
}

Mp4Maker::Mp4Maker(const string& strPath,
				   const string &strVhost,
				   const string &strApp,
				   const string &strStreamId,
				   const PlayerBase::Ptr &pPlayer) {
	DebugL << strPath;
	_pPlayer = pPlayer;
	_strPath = strPath;

	/////record 业务逻辑//////
	_info.strAppName = strApp;
	_info.strStreamId = strStreamId;
	_info.strVhost = strVhost;
	_info.strFolder = strPath;
	//----record 业务逻辑----//
}
Mp4Maker::~Mp4Maker() {
	closeFile();
}

void Mp4Maker::inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp, int iType){
	switch (iType) {
	case 1: //P
	case 5: { //IDR
		if (_strLastVideo.size()) {
			int64_t iTimeInc = (int64_t)ui32TimeStamp - (int64_t)_ui32LastVideoTime;
			iTimeInc = MAX(0,MIN(iTimeInc,500));
			if(iTimeInc == 0 ||  iTimeInc == 500){
				WarnL << "abnormal time stamp increment:" << ui32TimeStamp << " " << _ui32LastVideoTime;
			}
			_inputH264((char *) _strLastVideo.data(), _strLastVideo.size(), iTimeInc, _iLastVideoType);
		}
		//_strLastVideo.assign(("\x0\x0\x0\x2\x9\xf0"), 6);
		uint32_t *p = (uint32_t *) pData;
		*p = htonl(ui32Length - 4);
		_strLastVideo.assign((char *) pData, ui32Length);
		memcpy(pData, "\x00\x00\x00\x01", 4);

		_ui32LastVideoTime = ui32TimeStamp;
		_iLastVideoType = iType;
	}
		break;
	default:
		break;
	}
}
void Mp4Maker::inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp){
	if (_strLastAudio.size()) {
		int64_t iTimeInc = (int64_t)ui32TimeStamp - (int64_t)_ui32LastAudioTime;
		iTimeInc = MAX(0,MIN(iTimeInc,500));
		if(iTimeInc == 0 ||  iTimeInc == 500){
			WarnL << "abnormal time stamp increment:" << ui32TimeStamp << " " << _ui32LastAudioTime;
		}
		_inputAAC((char *)_strLastAudio.data(), _strLastAudio.size(), iTimeInc);
	}
	_strLastAudio.assign((char *)pData, ui32Length);
	_ui32LastAudioTime = ui32TimeStamp;
}

void Mp4Maker::_inputH264(void* pData, uint32_t ui32Length, uint32_t ui32Duration, int iType) {
    GET_CONFIG_AND_REGISTER(uint32_t,recordSec,Record::kFileSecond);

    if(iType == 5 && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)){
		//在I帧率处新建MP4文件
		//如果文件未创建或者文件超过10分钟则创建新文件
		createFile();
	}
	if (_hVideo != MP4_INVALID_TRACK_ID) {
		MP4WriteSample(_hMp4, _hVideo, (uint8_t *) pData, ui32Length,ui32Duration * 90,0,iType == 5);
	}
}

void Mp4Maker::_inputAAC(void* pData, uint32_t ui32Length, uint32_t ui32Duration) {
    GET_CONFIG_AND_REGISTER(uint32_t,recordSec,Record::kFileSecond);

	//todo(xzl) 修复此处

//
//    if (!_pPlayer->containVideo() && (_hMp4 == MP4_INVALID_FILE_HANDLE || _ticker.elapsedTime() > recordSec * 1000)) {
//		//在I帧率处新建MP4文件
//		//如果文件未创建或者文件超过10分钟则创建新文件
//		createFile();
//	}
//	if (_hAudio != MP4_INVALID_TRACK_ID) {
//		auto duration = ui32Duration * _pPlayer->getAudioSampleRate() /1000.0;
//		MP4WriteSample(_hMp4, _hAudio, (uint8_t*)pData + 7, ui32Length - 7,duration,0,false);
//	}
}

void Mp4Maker::createFile() {
	if(!_pPlayer->isInited()){
		return;
	}
	closeFile();

	auto strDate = timeStr("%Y-%m-%d");
	auto strTime = timeStr("%H-%M-%S");
	auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4";
	auto strFile =	_strPath + strDate + "/" + strTime + ".mp4";

	/////record 业务逻辑//////
	_info.ui64StartedTime = ::time(NULL);
	_info.strFileName = strTime + ".mp4";
	_info.strFilePath = strFile;

    GET_CONFIG_AND_REGISTER(string,appName,Record::kAppName);

    _info.strUrl = _info.strVhost + "/"
					+ appName + "/"
					+ _info.strAppName + "/"
					+ _info.strStreamId + "/"
					+ strDate + "/"
					+ strTime + ".mp4";
	//----record 业务逻辑----//

#if !defined(_WIN32)
	File::createfile_path(strFileTmp.data(), S_IRWXO | S_IRWXG | S_IRWXU);
#else
	File::createfile_path(strFileTmp.data(), 0);
#endif
	_hMp4 = MP4Create(strFileTmp.data());
	if (_hMp4 == MP4_INVALID_FILE_HANDLE) {
		WarnL << "创建MP4文件失败:" << strFileTmp;
		return;
	}
	//MP4SetTimeScale(_hMp4, 90000);
	_strFileTmp = strFileTmp;
	_strFile = strFile;
	_ticker.resetTime();

	//todo(xzl) 修复此处

//	if(_pPlayer->containVideo()){
//		auto &sps = _pPlayer->getSps();
//		auto &pps = _pPlayer->getPps();
//		_hVideo = MP4AddH264VideoTrack(_hMp4, 90000, MP4_INVALID_DURATION,
//				_pPlayer->getVideoWidth(), _pPlayer->getVideoHeight(),
//				sps[5], sps[6], sps[7], 3);
//		if(_hVideo !=MP4_INVALID_TRACK_ID){
//			MP4AddH264SequenceParameterSet(_hMp4, _hVideo, (uint8_t *)sps.data() + 4, sps.size() - 4);
//			MP4AddH264PictureParameterSet(_hMp4, _hVideo, (uint8_t *)pps.data() + 4, pps.size() - 4);
//		}else{
//			WarnL << "添加视频通道失败:" << strFileTmp;
//		}
//	}
//	if(_pPlayer->containAudio()){
//		_hAudio = MP4AddAudioTrack(_hMp4, _pPlayer->getAudioSampleRate(), MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE);
//		if (_hAudio != MP4_INVALID_TRACK_ID) {
//			auto &cfg =  _pPlayer->getAudioCfg();
//			MP4SetTrackESConfiguration(_hMp4, _hAudio,(uint8_t *)cfg.data(), cfg.size());
//		}else{
//			WarnL << "添加音频通道失败:" << strFileTmp;
//		}
//	}
}

void Mp4Maker::closeFile() {
	if (_hMp4 != MP4_INVALID_FILE_HANDLE) {
		{
			TimeTicker();
			MP4Close(_hMp4,MP4_CLOSE_DO_NOT_COMPUTE_BITRATE);
		}
		rename(_strFileTmp.data(),_strFile.data());
		_hMp4 = MP4_INVALID_FILE_HANDLE;
		_hVideo = MP4_INVALID_TRACK_ID;
		_hAudio = MP4_INVALID_TRACK_ID;

		/////record 业务逻辑//////
		_info.ui64TimeLen = ::time(NULL) - _info.ui64StartedTime;
		//获取文件大小
		struct stat fileData;
		stat(_strFile.data(), &fileData);
		_info.ui64FileSize = fileData.st_size;
		//----record 业务逻辑----//
		NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,_info,*this);
	}
}

} /* namespace mediakit */


#endif //ENABLE_MP4V2