namespace kos_gfx
{
	namespace storage
	{
		int number_of_vmus=0;
		
		class vmu_file
		{
			std::string filename;
			std::string app_id, description_short, description_long;
			
			bool has_icon;
			kos_gfx::Uint16 icon_pal[16];
			kos_gfx::Uint8 icon_img[512];
			
			void *buffer;
			int used_buffer_bytes, available_buffer_bytes;
			static const int DEFAULT_BUFFER_SIZE=512;
			
			public:
			vmu_file(std::string path)
			{
				filename=path;
				app_id=filename;
				description_short=filename;
				description_long=filename;
				
				app_id.resize(20);
				description_short.resize(20);
				description_long.resize(36);
				
				has_icon=false;
				
				buffer=malloc(DEFAULT_BUFFER_SIZE);
				available_buffer_bytes=DEFAULT_BUFFER_SIZE;
				used_buffer_bytes=0;
			};
			
			vmu_file(const vmu_file &other)
			{
				filename=other.filename;
				app_id=other.app_id;
				description_short=other.description_short;
				description_long=other.description_long;
				has_icon=other.has_icon;
				
				available_buffer_bytes=other.available_buffer_bytes;
				used_buffer_bytes=other.used_buffer_bytes;
				buffer=malloc(available_buffer_bytes);
				memcpy(buffer,other.buffer,used_buffer_bytes);
				
				memcpy(icon_pal,other.icon_pal,32);
				memcpy(icon_img,other.icon_img,512);
			};
			
			~vmu_file()
			{
				free(buffer);
			};
			
			void set_app_id(std::string new_id)
			{
				app_id=new_id;
				app_id.resize(20);
			};
			
			void set_short_description(std::string new_desc)
			{
				description_short=new_desc;
				description_short.resize(20);
			};
			
			void set_long_description(std::string new_desc)
			{
				description_long=new_desc;
				description_long.resize(36);
			};
			
			void write(void *buf, size_t buf_size)
			{
				if(used_buffer_bytes+buf_size>available_buffer_bytes)
				{
					void *tmp_buf=malloc(2*(used_buffer_bytes+buf_size));
					memcpy(tmp_buf,buffer,used_buffer_bytes);
					free(buffer);
					buffer=tmp_buf;
				};
				memcpy(buffer+used_buffer_bytes,buf,buf_size);
				used_buffer_bytes+=buf_size;
			};
			
			void read(void *buf, size_t requested_size)
			{
				requested_size=(used_buffer_bytes+requested_size>available_buffer_bytes?(available_buffer_bytes-used_buffer_bytes):requested_size);
				memcpy(buf,buffer+used_buffer_bytes,requested_size);
				used_buffer_bytes+=requested_size;
			};
			
			bool load(std::string vmu_dir)
			{
				file_t infile=fs_open((vmu_dir+filename).c_str(),O_RDONLY);
				if(infile==FILEHND_INVALID)
					return false;
				
				vmu_pkg_t vmupkg;
				int filesize=fs_total(infile);
				uint8 *data=(uint8*)malloc(filesize);
				fs_read(infile,data,filesize);
				vmu_pkg_parse(data,&vmupkg);
				
				free(buffer);
				used_buffer_bytes=0;
				available_buffer_bytes=vmupkg.data_len;
				buffer=malloc(available_buffer_bytes);
				memcpy(buffer,vmupkg.data,available_buffer_bytes);
				
				description_long=vmupkg.desc_long;
				description_short=vmupkg.desc_short;
				app_id=vmupkg.app_id;
				
				has_icon=vmupkg.icon_cnt>0;
				if(has_icon)
				{
					memcpy((void*)icon_pal,(void*)vmupkg.icon_pal[0],32);
					memcpy((void*)icon_img,(void*)vmupkg.icon_data,512);
				};
				
				free(data);
				fs_close(infile);
				
				return true;
			};
			
			bool save(std::string vmu_dir)
			{
				vmu_pkg_t savefile;
				kos_gfx::Uint8 *compiled_pkg;
				int pkg_size;
				
				strcpy(savefile.desc_short,description_short.c_str());
				strcpy(savefile.desc_long,description_long.c_str());
				strcpy(savefile.app_id,app_id.c_str());
					
				savefile.eyecatch_type = VMUPKG_EC_NONE;
				savefile.icon_cnt=has_icon?1:0;
				if(has_icon)
				{		
					memcpy((void*)&savefile.icon_pal[0],(void*)&icon_pal,32);
					savefile.icon_data=(const uint8*)&icon_img;
					savefile.icon_anim_speed=0;
				};
				
				savefile.data_len=used_buffer_bytes;
				savefile.data=(const uint8*)buffer;
				
				vmu_pkg_build(&savefile,&compiled_pkg,&pkg_size);
				
				file_t outfile=fs_open((vmu_dir+filename).c_str(),O_WRONLY);
				if(outfile==FILEHND_INVALID)
					return false;
				fs_write(outfile,compiled_pkg,pkg_size);
				fs_close(outfile);
				
				return true;
			};
		};
		
		class vmu
		{
			private:
			int port_id, unit_id;
			maple_device_t *device;
			
			std::string vfs_folder;
			
			bool is_connected,has_lcd;
			
			public:
			vmu(int p_id=0, int u_id=1)
			{
				port_id=p_id;
				unit_id=u_id;
				
				is_connected=false;
				has_lcd=false;
				
				vfs_folder="/vmu/";
				vfs_folder+=(char)('a'+p_id);
				vfs_folder+=(char)('0'+u_id);
				vfs_folder+='/';
				
				update();
			};
			
			void update()
			{
				if(is_connected)
					--kos_gfx::storage::number_of_vmus;
				
				is_connected=false;
				has_lcd=false;
				
				device=maple_enum_dev(port_id,unit_id);
				if(device!=NULL && (device->info.functions&MAPLE_FUNC_MEMCARD))
				{
					is_connected=true;
					if(device->info.functions&MAPLE_FUNC_LCD)
						has_lcd=true;
				};
				
				if(is_connected)
					++kos_gfx::storage::number_of_vmus;
			};
			
			bool connected()
			{
				update();
				return is_connected;
			};
			
			bool exists(std::string filename)
			{
				update();
				if(!is_connected)
					return false;
				
				int handle=fs_open((vfs_folder+filename).c_str(),O_RDONLY);
				if(handle==FILEHND_INVALID)
					return false;
				else
					fs_close(handle);
				return true;
			};
			
			bool save(vmu_file &file)
			{
				update();
				if(is_connected)
					return file.save(vfs_folder);
				else
					return false;
			};
			
			bool load(vmu_file &file)
			{
				update();
				if(is_connected)
					return file.load(vfs_folder);
				else
					return false;
			};
			
			//basically copied from kernel/arch/dreamcast/hardware/maple/vmu.c
			//there sadly wasnt a function to draw an xmb to a specific vmu ;-)
			void set_icon(const char *vmu_icon)
			{
				update();
				if(!has_lcd)
					return;
					
				int	xi, xb;
				kos_gfx::Uint8 bitmap[48*32/8];
				memset(bitmap, 0, 48*32/8);
				for(int y=0; y<32; y++)
				{
					for(int x=0; x<48; x++)
					{
						xi = x / 8;
						xb = 0x80 >> (x % 8);
						if (vmu_icon[(31-y)*48+(47-x)] == '.')
							bitmap[y*(48/8)+xi] |= xb;
					};
				};
				
				vmu_draw_lcd(device,bitmap);
			};
			
			static void set_icons(const char *vmu_icon)
			{
				vmu_set_icon(vmu_icon);
			};
			
		};
		
		vmu vmus[4];
		
		void init()
		{
			kos_gfx::storage::number_of_vmus=0;
			for(int i=0;i<4;++i)
			{
				vmus[i]=vmu(i);
			};
		};
		
		void shutdown()
		{
			kos_gfx::storage::number_of_vmus=0;
		};
	};
};
