/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Cyril Concolato / Jean Le Feuvre 2005
 *					All rights reserved
 *
 *  This file is part of GPAC / ISO Media File Format sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include <gpac/intern/m4_isomedia_dev.h>

ISMASample *ISMA_NewSample()
{
	ISMASample *tmp = (ISMASample *) malloc(sizeof(ISMASample));
	if (!tmp) return NULL;
	memset(tmp, 0, sizeof(ISMASample));
	return tmp;
}
void ISMA_DeleteSample(ISMASample *samp)
{
	if (!samp) return;
	if (samp->data && samp->dataLength) free(samp->data);
	if (samp->key_indicator) free(samp->key_indicator);
	free(samp);
}


ISMASample *M4_ParseISMASampleFromData(char *data, u32 dataLength, Bool use_selective_encryption, u8 KI_length, u8 IV_length)
{
	ISMASample *s;
	BitStream *bs;
	/*empty text sample*/
	if (!data || !dataLength) {
		return ISMA_NewSample();
	}
	
	s = ISMA_NewSample();
		
	/*empty sample*/
	if (!data || !dataLength) return s;

	bs = NewBitStream(data, dataLength, BS_READ);

	s->dataLength = dataLength;
	s->IV_length = IV_length;
	s->KI_length = KI_length;

	if (use_selective_encryption) {
		s->flags = ISMA_UseSelEnc;
		if (s->dataLength < 1) goto exit;
		if (BS_ReadInt(bs, 1)) s->flags |= ISMA_IsEncrypted;
		BS_ReadInt(bs, 7);
		s->dataLength -= 1;
	} else {
		s->flags = ISMA_IsEncrypted;
	}
	if (s->flags & ISMA_IsEncrypted) {
		if (IV_length != 0) {
			if (s->dataLength < IV_length) goto exit;
			s->IV = BS_ReadLongInt(bs, 8*IV_length);
			s->dataLength -= IV_length;
		}
		if (KI_length) {
			if (s->dataLength < KI_length) goto exit;
			s->key_indicator = (u8 *)malloc(KI_length);
			BS_ReadData(bs, s->key_indicator, KI_length);
			s->dataLength -= KI_length;
		}
	}
	s->data = malloc(s->dataLength);
	BS_ReadData(bs, s->data, s->dataLength);
	DeleteBitStream(bs);
	return s;

exit:
	ISMA_DeleteSample(s);
	return NULL;
}

M4Err M4_ISMASampleToSample(ISMASample *s, M4Sample *dest)
{
	BitStream *bs;
	if (!s || !dest) return M4BadParam;

	bs = NewBitStream(NULL, 0, BS_WRITE);

	if (s->flags & ISMA_UseSelEnc) {
		BS_WriteInt(bs, (s->flags & ISMA_IsEncrypted) ? 1 : 0, 1);
		BS_WriteInt(bs, 0, 7);
	} 
	if (s->flags & ISMA_IsEncrypted) {
		if (s->IV_length) BS_WriteLongInt(bs, s->IV, 8*s->IV_length);
		if (s->KI_length) BS_WriteData(bs, s->key_indicator, s->KI_length);
	}
	BS_WriteData(bs, s->data, s->dataLength);
	if (dest->data) free(dest->data);
	dest->data = NULL;
	dest->dataLength = 0;
	BS_GetContent(bs, (unsigned char **) &dest->data, &dest->dataLength);
	DeleteBitStream(bs);
	return M4OK;
}

ISMASample *M4_GetISMACrypSample(M4File *the_file, u32 trackNumber, M4Sample *samp, u32 sampleDescriptionIndex)
{
	TrackAtom *trak;
	SampleEntryAtom *sea;
	
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return NULL;

	Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea || !sea->protection_info 
		|| !sea->protection_info->scheme_type 
		|| (sea->protection_info->scheme_type->scheme_type != M4_ISMA_EncSchemeType)
		|| !sea->protection_info->info
		|| !sea->protection_info->info->ikms
		|| !sea->protection_info->info->isfm
		) {
		return NULL;
	}
	/*OK*/
	return M4_ParseISMASampleFromData(samp->data, samp->dataLength, sea->protection_info->info->isfm->selective_encryption, sea->protection_info->info->isfm->key_indicator_length, sea->protection_info->info->isfm->IV_length);
}


Bool M4_IsCryptedMedia(M4File *the_file, u32 trackNumber, u32 sampleDescriptionIndex)
{
	TrackAtom *trak;
	SampleEntryAtom *sea;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return 0;

	Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea || !sea->protection_info) return 0;
	return 1;
}

Bool M4_IsISMACrypMedia(M4File *the_file, u32 trackNumber, u32 sampleDescriptionIndex)
{
	TrackAtom *trak;
	SampleEntryAtom *sea;

	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return 0;

	Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea 
		|| !sea->protection_info 
		|| !sea->protection_info->scheme_type 
		|| (sea->protection_info->scheme_type->scheme_type != M4_ISMA_EncSchemeType)
		|| !sea->protection_info->info
		|| !sea->protection_info->info->ikms
		|| !sea->protection_info->info->isfm
		) 
		return 0;

	return 1;
}

/*retrieves ISMACryp info for the given track & SDI*/
M4Err M4_GetISMACrypInfo(M4File *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outSchemeURI, const char **outKMS_URI, Bool *outSelectiveEncryption, u8 *outIVLength, u8 *outKeyIndicationLength)
{
	TrackAtom *trak;
	SampleEntryAtom *sea;
	
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return M4BadParam;

	Media_GetSampleDesc(trak->Media, sampleDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea || !sea->protection_info) return M4BadParam;
	if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return M4NonCompliantBitStream;

	if (outOriginalFormat) {
		*outOriginalFormat = sea->protection_info->original_format->data_format;
		if (IsMP4Description(sea->protection_info->original_format->data_format)) *outOriginalFormat = M4_MPEG4_SubType;
	}
	if (outSchemeType) *outSchemeType = sea->protection_info->scheme_type->scheme_type;
	if (outSchemeVersion) *outSchemeVersion = sea->protection_info->scheme_type->scheme_version;
	if (outSchemeURI) *outSchemeURI = sea->protection_info->scheme_type->URI;

	if (sea->protection_info->info && sea->protection_info->info->ikms) {
		if (outKMS_URI) *outKMS_URI = sea->protection_info->info->ikms->URI;
	} else {
		if (outKMS_URI) *outKMS_URI = NULL;
	}
	if (sea->protection_info->info && sea->protection_info->info->isfm) {
		if (outSelectiveEncryption) *outSelectiveEncryption = sea->protection_info->info->isfm->selective_encryption;
		if (outIVLength) *outIVLength = sea->protection_info->info->isfm->IV_length;
		if (outKeyIndicationLength) *outKeyIndicationLength = sea->protection_info->info->isfm->key_indicator_length;
	} else {
		if (outSelectiveEncryption) *outSelectiveEncryption = 0;
		if (outIVLength) *outIVLength = 0;
		if (outKeyIndicationLength) *outKeyIndicationLength = 0;
	}
	return M4OK;
}

#ifndef M4_READ_ONLY

M4Err M4_RemoveISMACrypProtection(M4File *the_file, u32 trackNumber, u32 StreamDescriptionIndex)
{
	TrackAtom *trak;
	M4Err e;
	SampleEntryAtom *sea;

	e = CanAccessMovie(the_file, M4_OPEN_WRITE);
	if (e) return e;
	
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !trak->Media || !StreamDescriptionIndex) return M4BadParam;

	Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea || !sea->protection_info) return M4BadParam;
	if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return M4NonCompliantBitStream;

	sea->type = sea->protection_info->original_format->data_format;
	DelAtom((Atom *)sea->protection_info);
	sea->protection_info = NULL;
	return M4OK;
}

M4Err M4_ChangeISMACrypProtection(M4File *the_file, u32 trackNumber, u32 StreamDescriptionIndex, char *scheme_uri, char *kms_uri)
{
	TrackAtom *trak;
	M4Err e;
	SampleEntryAtom *sea;

	e = CanAccessMovie(the_file, M4_OPEN_WRITE);
	if (e) return e;
	
	trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak || !trak->Media || !StreamDescriptionIndex) return M4BadParam;

	Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &sea, NULL);
	/*non-encrypted or non-ISMA*/
	if (!sea || !sea->protection_info) return M4BadParam;
	if (!sea->protection_info->scheme_type || !sea->protection_info->original_format) return M4NonCompliantBitStream;

	if (scheme_uri) {
		free(sea->protection_info->scheme_type->URI);
		sea->protection_info->scheme_type->URI = strdup(scheme_uri);
	}
	if (kms_uri) {
		free(sea->protection_info->info->ikms->URI);
		sea->protection_info->info->ikms->URI = strdup(kms_uri);
	}
	return M4OK;
}


M4Err M4_SetISMAProtection(M4File *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, 
						   u32 scheme_version, char *scheme_uri, char *kms_URI,
						   Bool selective_encryption, u32 KI_length, u32 IV_length)
{
	u32 original_format;
	M4Err e;
	SampleEntryAtom *sea;
	TrackAtom *trak = GetTrackFromFile(the_file, trackNumber);
	if (!trak) return M4BadParam;

	e = Media_GetSampleDesc(trak->Media, desc_index, &sea, NULL);
	if (e) return e;

	/* Replacing the Media Type */
	switch (sea->type) {
	case MPEGAudioSampleEntryAtomType:
		original_format = sea->type;
		sea->type = MPEGEncAudioSampleEntryAtomType;
		break;
	case MPEGVisualSampleEntryAtomType:
		original_format = sea->type;
		sea->type = MPEGEncVisualSampleEntryAtomType;
		break;
	case MPEGSampleEntryAtomType:
		original_format = sea->type;
		sea->type = MPEGEncSystemsSampleEntryAtomType;
		break;
	default:
		return M4BadParam;
	}
	
	sea->protection_info = (ProtectionInfoAtom *)sinf_New();
	sea->protection_info->scheme_type = (SchemeTypeAtom *)schm_New();
	sea->protection_info->scheme_type->scheme_type = scheme_type;
	sea->protection_info->scheme_type->scheme_version = scheme_version;
	if (scheme_uri) {
		sea->protection_info->scheme_type->flags |= 0x000001;
		sea->protection_info->scheme_type->URI = strdup(scheme_uri);
	}
	sea->protection_info->original_format = (OriginalFormatAtom *)frma_New();
	sea->protection_info->original_format->data_format = original_format;
	sea->protection_info->info = (SchemeInformationAtom *)schi_New();

	sea->protection_info->info->ikms = (ISMAKMSAtom *)iKMS_New();
	sea->protection_info->info->ikms->URI = strdup(kms_URI);

	sea->protection_info->info->isfm = (ISMASampleFormatAtom *)iSFM_New();
	sea->protection_info->info->isfm->selective_encryption = selective_encryption;
	sea->protection_info->info->isfm->key_indicator_length = KI_length;
	sea->protection_info->info->isfm->IV_length = IV_length;
	return M4OK;
}

#endif
