/*Copyright (C) 2005-2006 Michael Kasianowicz (xtravar@yahoo.com)

This program 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
of the License, or (at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <p2kcmd_fsac.h>
#include <string.h>

#include <stdio.h>

/* #define _P2K_DEBUG */

static int __p2k_trans(p2k_phone *ph, p2k_packet *ps, p2k_packet **ppr, p2k_result result) {
	int retval;
#ifdef _P2K_DEBUG
	printf("Sending->\n");
	p2k_packet_print(ps);
#endif
	if(p2k_phone_send(ph, ps) < 0) {
#ifdef _P2K_DEBUG
		printf("Sending failed\n");
#endif
		retval = -1;
		goto DESTROY_ONE;
	}
	
	p2k_packet *pr = (p2k_packet*)p2k_phone_recv(ph);
	if(pr == (p2k_packet*)NULL) {
#ifdef _P2K_DEBUG
		printf("Packet is NULL\n");
#endif
		retval = -1;
		goto DESTROY_ONE;
	}
#ifdef _P2K_DEBUG
	printf("Received->\n");
	p2k_packet_print(pr);
#endif
	if(p2k_packet_get_id(pr) != p2k_packet_get_id(ps)) {
#ifdef _P2K_DEBUG
		printf("Packet ID mismatch\n");
#endif
		retval = -1;
		goto DESTROY_TWO;
	}

	if(p2k_packet_get_result(pr) != result) {
#ifdef _P2K_DEBUG
		printf("Unexpected result\n");
#endif

		retval = -1;
		goto DESTROY_TWO;
	}

	*ppr = pr;
	retval = 0;
	goto DESTROY_ONE;

DESTROY_TWO:
	p2k_packet_destroy(pr);
DESTROY_ONE:
	p2k_packet_destroy(ps);
	return retval;
}

static int __p2k_trans_data(p2k_phone *ph, p2k_packet *ps, p2k_packet **ppr) {
	return __p2k_trans(ph, ps, ppr, P2K_RESULT_DATA);
}

static int __p2k_trans_nodata(p2k_phone *ph, p2k_packet *ps) {
	int retval;
	p2k_packet *pr;
	retval = __p2k_trans(ph, ps, &pr, P2K_RESULT_NODATA);
	if(retval == 0) {
		p2k_packet_destroy(pr);
	}
	return retval;
}

/* returns 0 on success */
int p2k_fsac_open(p2k_phone *ph, const char *fname, p2k_file_attribute attr) {
	int retval;
	int pos = 0;
	int len = strlen(fname);
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 8 + len);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_OPEN);
	p2k_packet_put_u16(ps, &pos, 0x0000);
	p2k_packet_put_u16(ps, &pos, attr);
	p2k_packet_put_str(ps, &pos, fname);
	p2k_packet_set_data_size(ps, pos);

	return __p2k_trans_nodata(ph, ps);
}


int p2k_fsac_read(p2k_phone *ph, uint8_t *buffer, int count) {
	if(count < 0) {
		return -1;
	}

	if(count > 0x4000) {
		count = 0x4000;
	}

	int retval;
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 8);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_READ);
	p2k_packet_put_u16(ps, &pos, 0x0000);
	p2k_packet_put_u16(ps, &pos, (uint16_t)count);
	p2k_packet_set_data_size(ps, pos);	

	p2k_packet *pr;
	retval = __p2k_trans_data(ph, ps, &pr);
	if(retval == 0) {
		pos = 0;
		retval = p2k_packet_get_buff(pr, &pos, buffer, p2k_packet_get_data_size(pr));
		p2k_packet_destroy(pr);
	}
	return retval;
}

int p2k_fsac_write(p2k_phone *ph, uint8_t *buffer, int count) {
	if(count < 0) {
		return -1;
	}

	if(count > 0x4000) {
		count = 0x4000;
	}

	int retval;
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 8 + count);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_WRITE);
	p2k_packet_put_u16(ps, &pos, 0x0000);
	p2k_packet_put_u16(ps, &pos, (uint16_t)count);
	p2k_packet_put_buff(ps, &pos, buffer, count);
	p2k_packet_set_data_size(ps, pos);

	retval = __p2k_trans_nodata(ph, ps);
	if(retval == 0) {
		return count;
	} else {
		return retval;
	}
}

int p2k_fsac_close(p2k_phone *ph) {
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 4);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_CLOSE);
	p2k_packet_set_data_size(ps, pos);

	return __p2k_trans_nodata(ph, ps);
}

off_t p2k_fsac_seek(p2k_phone *ph, off_t offset, p2k_seek_dir dir) {
	int retval;
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 9);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_SEEK);
	p2k_packet_put_u32(ps, &pos, offset);
	p2k_packet_put_u8(ps, &pos, (uint8_t)dir);
	p2k_packet_set_data_size(ps, pos);

	retval = __p2k_trans_nodata(ph, ps);
	if(retval == 0) {
		return offset;
	} else {
		return retval;
	}
}

int p2k_fsac_delete(p2k_phone *ph, const char *path) {
	int retval;
	int pos = 0;
	int nlen = strlen(path);
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 4 + nlen);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_DELETE);
	p2k_packet_put_str(ps, &pos, path);
	p2k_packet_set_data_size(ps, pos);
	
	return __p2k_trans_nodata(ph, ps);
}

int p2k_fsac_count(p2k_phone *ph) {
	int retval;
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 4);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_COUNT);
	p2k_packet_set_data_size(ps, pos);
	
	p2k_packet *pr;
	retval = __p2k_trans_data(ph, ps, &pr);
	if(retval == 0) {
		uint16_t retval2;
		pos = 0;
		if(p2k_packet_get_u16(pr, &pos, &retval2) == 0) {
			retval = -1;
		} else {
			retval = retval2;
		}
		p2k_packet_destroy(pr);
	}
	return retval;
}

int p2k_fsac_list(p2k_phone *ph, int (*callback)(int, int, int, int, int, const char*)) {
	/* believe it or not, count is required */
	int count = p2k_fsac_count(ph);
	int retval;
	int pos;
	p2k_packet *pr;
	uint16_t rec_count;
	p2k_packet *ps;

	int current;

	for(current = 0; current < count; current++) {
		pos = 0;
		ps = p2k_packet_create(P2K_FSAC, 4);
		p2k_packet_put_u32(ps, &pos, P2K_FSAC_LIST);
		p2k_packet_set_data_size(ps, pos);
		retval = __p2k_trans_data(ph, ps, &pr);
		if(retval < 0) {
			break;
		}
		pos = 0;
		if(p2k_packet_get_u16(pr, &pos, (uint16_t*)&rec_count) == 0) {
			retval = -1;
			break;
		}
		/* skip 2 bytes of nothing. perhaps the last of a 32 that doesnt use all the bytes? */
		pos += 2;

		int reclen = (p2k_packet_get_data_size(pr) - 4) / rec_count;
		int namelen = reclen - 8;
		char name[1024];
		uint16_t attrib;
		uint16_t owner;
		uint32_t length;
		int i;
		for(i = 0; i < rec_count; i++) {
			if(p2k_packet_get_str(pr, &pos, name, namelen) == 0 || 
				p2k_packet_get_u16(pr, &pos, &attrib) == 0 ||
				p2k_packet_get_u16(pr, &pos, &owner) == 0 || 
				p2k_packet_get_u32(pr, &pos, &length) == 0) {
				p2k_packet_destroy(pr);
				return -1;
			}
			current++;	
			if(callback(current, count, length, attrib, owner, name)) {
				current = count;
			}
		}
		p2k_packet_destroy(pr);
	}

	return 0;
}

int p2k_fsac_volname(p2k_phone *ph, char *output, int outmax) {
	int retval;
	int pos = 0;
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 4);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_VOLNAME);
	p2k_packet_set_data_size(ps, pos);

	p2k_packet *pr;
	retval = __p2k_trans_data(ph, ps, &pr);

	if(retval == 0) {
		int sz = p2k_packet_get_data_size(pr) / 2;
		if(outmax > sz) {
			outmax = sz;
		}
		pos = 0;
		retval = p2k_packet_get_wstr_a(pr, &pos, output, outmax);
		if(retval == 0) {
			retval = -1;
		}
		p2k_packet_destroy(pr);
	}
	return retval;
}

int p2k_fsac_volspace(p2k_phone *ph, const char *volume) {
	int retval;
	int pos = 0;
	int nlen = strlen(volume);
	if(nlen > 0x00FE) {
		/* Drive name exceeds max length */
		return -1;
	}
	p2k_packet *ps = p2k_packet_create(P2K_FSAC, 4 + 0x00FE);
	p2k_packet_put_u32(ps, &pos, P2K_FSAC_VOLSPACE);
	p2k_packet_put_strpad(ps, &pos, volume, 0x00FE);
	p2k_packet_set_data_size(ps, pos);

	p2k_packet *pr;
	retval = __p2k_trans_data(ph, ps, &pr);
	
	if(retval == 0) {
		pos = 0;
		if(p2k_packet_get_u32(pr, &pos, (uint32_t*)&retval) == 0) {
			retval = -1;
		}
		p2k_packet_destroy(pr);
	}

	return retval;
}


