// basis_wrappers.cpp - Simple C-style wrappers to the C++ transcoder for WebGL use. 
#include "basisu_transcoder.h"

using namespace basist;

static basist::etc1_global_selector_codebook *g_pGlobal_codebook;

typedef unsigned int uint;

extern "C" 
{
  void basis_init();
  
  void *basis_open(void *src, uint src_size);
  void basis_close(void *h);

  uint basis_get_has_alpha(void *h);
	
  uint basis_get_num_images(void *h);
  uint basis_get_num_levels(void *h, uint image_index);
  
  uint basis_get_image_width(void *h, uint image_index, uint level_index);
  uint basis_get_image_height(void *h, uint image_index, uint level_index);
         
  uint basis_get_image_transcoded_size_in_bytes(void *h, uint image_index, uint level_index, uint format);
  
  uint basis_start_transcoding(void *h);
  uint basis_stop_transcoding(void *h);
  
  uint basis_transcode_image(void *h, void *dst, uint dst_size_in_bytes, 
  uint image_index, uint level_index, uint format, 
  uint unused, uint get_alpha_for_opaque_formats);
  
  void basis_set_debug_flags(uint f) { basist::set_debug_flags(f); }
  uint basis_get_debug_flags() { return basist::get_debug_flags(); }
}

void basis_init()
{
	basisu_transcoder_init();
	
	if (!g_pGlobal_codebook)
		g_pGlobal_codebook = new basist::etc1_global_selector_codebook(g_global_selector_cb_size, g_global_selector_cb);
}

#define MAGIC 0xDEADBEE1

struct basis_file
{
	int m_magic;
	basisu_transcoder m_transcoder;
	void *m_pFile;
	uint m_file_size;

	basis_file() : 
		m_transcoder(g_pGlobal_codebook)
	{
	}
};

void *basis_open(void *src, uint src_size)
{
	if ((!src) || (!src_size))
		return nullptr;
		
	basis_file *f = new basis_file;
	f->m_pFile = src;
	f->m_file_size = src_size;
	
	if (!f->m_transcoder.validate_header(f->m_pFile, f->m_file_size))
	{
		delete f;
		return nullptr;
	}
	f->m_magic = MAGIC;
	
	return f;
}

void basis_close(void *h)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return;
	
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return;
	
	delete f;
}

uint basis_get_has_alpha(void *h)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;

	assert(f->m_magic == MAGIC);	
	if (f->m_magic != MAGIC)
		return 0;
	
	basisu_image_level_info li;
	
	if (!f->m_transcoder.get_image_level_info(f->m_pFile, f->m_file_size, li, 0, 0))
		return 0;
		
	return li.m_alpha_flag;
}

uint basis_get_num_images(void *h)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
	
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;
	
	return f->m_transcoder.get_total_images(f->m_pFile, f->m_file_size);
}

uint basis_get_num_levels(void *h, uint image_index)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
	
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;
	
	basisu_image_info ii;
	
	if (!f->m_transcoder.get_image_info(f->m_pFile, f->m_file_size, ii, image_index))
		return 0;
		
	return ii.m_total_levels;
}

uint basis_get_image_width(void *h, uint image_index, uint level_index)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
	
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;

	uint orig_width, orig_height, total_blocks;	
	if (!f->m_transcoder.get_image_level_desc(f->m_pFile, f->m_file_size, image_index, level_index, orig_width, orig_height, total_blocks))
		return 0;
	
	return orig_width;
}

uint basis_get_image_height(void *h, uint image_index, uint level_index)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
	
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;		
	
	uint orig_width, orig_height, total_blocks;	
	if (!f->m_transcoder.get_image_level_desc(f->m_pFile, f->m_file_size, image_index, level_index, orig_width, orig_height, total_blocks))
		return 0;
	
	return orig_height;
}

uint basis_get_image_transcoded_size_in_bytes(void *h, uint image_index, uint level_index, uint format)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
	
	assert(f->m_magic == MAGIC);	
	if (f->m_magic != MAGIC)
		return 0;	
	
	if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats)
		return 0;
			
	uint orig_width, orig_height, total_blocks;	
	if (!f->m_transcoder.get_image_level_desc(f->m_pFile, f->m_file_size, image_index, level_index, orig_width, orig_height, total_blocks))
		return 0;
   
   uint bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel((transcoder_texture_format)format);   
   if (basis_transcoder_format_is_uncompressed((transcoder_texture_format)format))
      return orig_width * orig_height * bytes_per_block_or_pixel;
	
	return total_blocks * bytes_per_block_or_pixel;
}

uint basis_start_transcoding(void *h)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
    
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;	
		
	return f->m_transcoder.start_transcoding(f->m_pFile, f->m_file_size);
}

uint basis_stop_transcoding(void *h)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
    
	assert(f->m_magic == MAGIC);
	if (f->m_magic != MAGIC)
		return 0;	
		
	return f->m_transcoder.stop_transcoding();
}

uint basis_transcode_image(void *h, void *dst, uint dst_size_in_bytes, 
	uint image_index, uint level_index, uint format, 
	uint unused, uint get_alpha_for_opaque_formats)
{
	basis_file *f = static_cast<basis_file *>(h);
	if (!f)
		return 0;
		
	assert(f->m_magic == MAGIC);	
	if (f->m_magic != MAGIC)
		return 0;
		
	if (format >= (int)transcoder_texture_format::cTFTotalTextureFormats)
		return 0;
		
	uint bytes_per_block = basis_get_bytes_per_block_or_pixel((transcoder_texture_format)format);
	
	return f->m_transcoder.transcode_image_level(f->m_pFile, f->m_file_size, image_index, level_index,
		dst, dst_size_in_bytes / bytes_per_block,
		(basist::transcoder_texture_format)format, 
		(get_alpha_for_opaque_formats ? (uint32_t)cDecodeFlagsTranscodeAlphaDataToOpaqueFormats : 0));
}
