/*
 * Copyright (C) 2007-2008 Arnaud Ysmal <wiidevel@stacktic.org>
 *
 * miifunctions.c (TransferMii)
 *
 * 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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include "tmii.h"

#define MII_CREATOR 0
#define MII_NAME 1

extern bool wm_read;
extern bool wm_update;

bool big_endian = 0;

unsigned char mii[MII_SIZE];
unsigned char miiblock[BLOCK_SIZE];
unsigned char wii_mac[3];

/* *miibuf must have a size of MII_SIZE */
/* if miibuf is NULL the mii is copied to the mii array */
bool getMii(unsigned char *miibuf, int slot)
{	
	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	if (!wm_read && !wiimoteRead())
		return FALSE;
	
	memcpy(miibuf?miibuf:mii, miiAddress(slot), MII_SIZE);

	return TRUE;
}

static bool getMiiString(uint16_t *str, int slot, bool name)
{
	unsigned char *src;
	
	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;	
	}
	if (!str) {
		fprintf(stderr, "null pointer\n");
		return FALSE;
	}
	if (slot) {
		if (!wm_read && !wiimoteRead())
			return FALSE;
		src = miiAddress(slot)+(name?MII_NAME_OFFSET:MII_CREATOR_OFFSET);
	} else {
		src = mii+(name?MII_NAME_OFFSET:MII_CREATOR_OFFSET);
	}
	return !memcpy(str, src, MII_CREATOR_LENGTH_BYTE);
}

static bool setMiiString(uint16_t *str, int slot, bool name)
{
	unsigned char *dest;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;	
	}
	if (!str) {
		fprintf(stderr, "null poiter\n");
		return FALSE;
	}
	if (slot) {
		if (!wm_read && !wiimoteRead())
			return FALSE;
		dest = miiAddress(slot)+(name?MII_NAME_OFFSET:MII_CREATOR_OFFSET);
	} else {
		dest = mii+(name?MII_NAME_OFFSET:MII_CREATOR_OFFSET);
	}
		
        return !memcpy(dest, str, MII_CREATOR_LENGTH_BYTE);
}

/* *creatorsname must have a size of MII_CREATOR_LENGTH_BYTE+1 */
bool getMiiCreator(uint16_t *creatorsname, int slot)
{
	int i = 1;
	
	big_endian=!*(char*)&i;
	getMiiString(creatorsname, slot, MII_CREATOR);
	if (!big_endian) {
		for (i=0; i<MII_CREATOR_LENGTH_CHAR; i++)
			creatorsname[i] = swap16(creatorsname[i]);
	}
	return TRUE;
}

/* *creatorsname must have a size of MII_CREATOR_LENGTH_CHAR+1 */
bool getMiiCreatorAscii(char *creatorsname, int slot)
{
	uint16_t tmp[MII_CREATOR_LENGTH_CHAR];
		
	getMiiString(tmp, slot, MII_CREATOR); 
	beu16toascii(tmp, creatorsname);

	return TRUE;
}

bool setMiiCreator(uint16_t *creatorsname, int slot)
{
	uint16_t tmp[MII_CREATOR_LENGTH_CHAR];
	int i = 1;

	memcpy(tmp, creatorsname, MII_CREATOR_LENGTH_BYTE);

	big_endian=!*(char*)&i;
	for (i=0; i<MII_CREATOR_LENGTH_CHAR; i++)
		tmp[i] = swap16(tmp[i]);

	return setMiiString(tmp, slot, 0);
}

bool setMiiCreatorAscii(char *creatorsname, int slot)
{
	uint16_t creator[MII_CREATOR_LENGTH_CHAR];

	asciitobeu16(creatorsname, creator);
	return setMiiString(creator, slot, 0);
}



bool getMiiName(uint16_t *miisname, int slot)
{
	int i = 1;

	getMiiString(miisname, slot, MII_NAME);

	big_endian=!*(char*)&i;
	if (!big_endian) {
		for (i=0; i<MII_CREATOR_LENGTH_CHAR; i++)
			miisname[i] = swap16(miisname[i]);
	}
	return TRUE;
}

bool getMiiNameAscii(char *miisname, int slot)
{
	uint16_t name[MII_NAME_LENGTH_CHAR];

	getMiiString(name, slot, MII_NAME);
	beu16toascii(name, miisname);

	return TRUE;
}

bool setMiiName(uint16_t *miisname, int slot)
{
	uint16_t name[MII_NAME_LENGTH_CHAR];
	int i = 1;

	memcpy(name, miisname, MII_NAME_LENGTH_BYTE);

	big_endian=!*(char*)&i;

	if (!big_endian) {
		for (i=0; i<MII_CREATOR_LENGTH_CHAR; i++)
			name[i] = swap16(name[i]);
	}

	return setMiiString(name, slot, MII_NAME);
}

bool setMiiNameAscii(char *miisname, int slot)
{
	uint16_t name[MII_CREATOR_LENGTH_CHAR];
	
	asciitobeu16(miisname, name);

	return setMiiString(name, slot, MII_NAME);
}

/* Returns 'M' for a Male and 'F' for a Female*/
char getMiiSex(int slot)
{
	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	if (slot) {
		if (!wm_read && !wiimoteRead())
			return FALSE;
		return (*miiAddress(slot)&0x40)?'F':'M';
	} else {
		return (*mii&0x40)?'F':'M';
	}
}

bool setMiiSex(char sex, int slot)
{
	unsigned char *pmii;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	if (sex!='F' && sex!='M') {
		fprintf(stderr, "Sex inccorrect\n");
		return FALSE;
	}
	if (slot) {
		if (!wm_read && !wiimoteRead())
			return FALSE;
		pmii=miiAddress(slot);
	} else {
		pmii=mii;
	}
	if (sex=='F' && !(*pmii&0x40))
		*pmii|=0x40;
	else if (sex=='M' && *pmii&0x40)
		*pmii^=0x40;
	return TRUE;
}

/* Lists mii on the wiimote */
bool listMiis()
{
	char name[MII_NAME_LENGTH_CHAR];
	int i;
	
	if (!wm_read && !wiimoteRead())
		return FALSE;
	for (i=1; i<=MII_NB; i++) {
		printf("Slot %i\t", i);
		if (miiIsEmpty(i)) {
			printf("empty\n");
			continue;
		} 
		getMiiNameAscii(name, i);
		printf("Name : %s\t", name);
		getMiiCreatorAscii(name, i);
		printf("Creator : %s\t", name);
		printf("Sex : %s\n", (getMiiSex(i)=='F')?"Female":"Male");
	}
	return TRUE;
}

/* Sets the new mac address */
/* slot 0    : updates mac address of the mii array */
/* slot 1-10 : updates corresponding mii on miiblock array */
bool setMacAddress(int slot)
{
	unsigned char* pmac;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	if (!*(short*)wii_mac)
		return TRUE;
	if (slot) {
		if (!wm_read && !wiimoteRead())
			return FALSE;
		pmac = miiAddress(slot)+MAC_OFFSET;
		wm_update=TRUE;
	} else {
		pmac=mii+MAC_OFFSET;
	}

	if (*pmac != MAC_SUM) {
		printf("Macsum incorrect, Correcting...\n");
		*pmac=MAC_SUM;
	}
	pmac[1] = wii_mac[0];
	pmac[2] = wii_mac[1];
	pmac[3] = wii_mac[2];
	return TRUE;
}

bool getMiiMacAddress(unsigned char *macaddr, int slot)
{
	unsigned char *pmac;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	pmac = (slot?miiAddress(slot):mii)+MAC_OFFSET;
	macaddr[0] = *(++pmac);
	macaddr[1] = *(++pmac);
	macaddr[2] = *(++pmac);
	return TRUE;
}

/* Adds mii */
/* Slot 0 : looks for the first empty slot */
bool addMii(char* miifile, int slot)
{
	unsigned char* pmii;
	
	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	if (!fileRead(miifile) || (!wm_read && !wiimoteRead()))
		return FALSE;

	if (slot==0 && !(slot=nextEmptySlot())) {
		fprintf(stderr, "No empty slot\n");
		return FALSE;
	}
	pmii = miiAddress(slot);
	if (!miiIsEmpty(slot)) {
		printf("Slot is not empty, overwrite ? (y/n) : ");
		if (getchar() != 'y')
			return FALSE;
	}

	printf("Adding mii from %s to slot %i...\n", miifile, slot);
        if (!memcpy(pmii, mii, MII_SIZE)) {
		fprintf(stderr, "Adding mii to miiblock error\n");
		return FALSE;
	}

	miiblock[4] = 0;
	miiblock[5] = 0;

	return wm_update=TRUE;
}

bool delMii(int slot)
{
	unsigned char *pmii;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	pmii = slot?miiAddress(slot):mii;
	memset(pmii, 0, MII_SIZE);
	return TRUE;
}

bool miiIsEmpty(int slot)
{
	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;
	}
	return !((slot&&*(int*)miiAddress(slot))||(!slot&&*(int*)mii));

}

bool miiSeemsValid(int slot)
{
	unsigned char *pmii;

	if (slot<0 || slot>10) {
		fprintf(stderr, "Slot incorrect\n");
		return FALSE;	
	}
	
	pmii = slot?miiAddress(slot):mii;

	if (*pmii&0x80) {
		fprintf(stderr, "invalid bit is set\n");
		return FALSE;
	}
	if (*(pmii+=MII_NAME_OFFSET+MII_NAME_LENGTH_BYTE) > 0x7F) {
		fprintf(stderr, "height error\n");
		return FALSE;
	}
	if (*(++pmii) > 0x7F) {
		fprintf(stderr, "weight error\n");
		return FALSE;
	}	
	return TRUE;
}
