/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 "glk/alan2/types.h"
#include "glk/alan2/main.h"
#include "glk/alan2/reverse.h"

namespace Glk {
namespace Alan2 {

/*----------------------------------------------------------------------

  reversed()

  Return the reversed bytes in the Aword

*/
Aword reversed(Aword w /* IN - The ACODE word to swap bytes of */) {
	Aword s;                      /* The swapped ACODE word */
	char *wp, *sp;

	wp = (char *) &w;
	sp = (char *) &s;

	for (uint i = 0; i < sizeof(Aword); i++)
		sp[sizeof(Aword) - 1 - i] = wp[i];

	return s;
}

void reverse(Aword *w /* IN - The ACODE word to reverse bytes in */) {
	*w = reversed(*w);
}

static void reverseTable(Aword adr, int len) {
	Aword *e = &memory[adr];
	int i;

	if (adr != 0)
		while (!endOfTable(e)) {
			for (i = 0; i < len / (int)sizeof(Aword); i++) {
				reverse(e);
				e++;
			}
		}
}

static void reverseStms(Aword adr) {
	Aword *e = &memory[adr];

	if (adr != 0)
		while (TRUE) {
			reverse(e);
			if (*e == ((Aword)C_STMOP << 28 | (Aword)I_RETURN)) break;
			e++;
		}
}

static void reverseMsgs(Aword adr) {
	MsgElem *e = (MsgElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(MsgElem));
		while (!endOfTable(e)) {
			reverseStms(e->stms);
			e++;
		}
	}
}

static void reverseWrds(Aword adr) {
	WrdElem *e = (WrdElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(WrdElem));
		while (!endOfTable(e)) {
			if ((e->_class & (1L << WRD_SYN)) == 0) { /* Do not do this for synonyms */
				reverseTable(e->adjrefs, sizeof(Aword));
				reverseTable(e->nounrefs, sizeof(Aword));
			}
			e++;
		}
	}
}

static void reverseChks(Aword adr) {
	ChkElem *e = (ChkElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ChkElem));
		while (!endOfTable(e)) {
			reverseStms(e->exp);
			reverseStms(e->stms);
			e++;
		}
	}
}

static void reverseAlts(Aword adr) {
	AltElem *e = (AltElem *)&memory[adr];

	if (adr != 0 && !endOfTable(e) && !e->done) {
		reverseTable(adr, sizeof(AltElem));
		e->done = TRUE;
		while (!endOfTable(e)) {
			reverseChks(e->checks);
			reverseStms(e->action);
			e++;
		}
	}
}

static void reverseVrbs(Aword adr) {
	VrbElem *e = (VrbElem *)&memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(VrbElem));
		while (!endOfTable(e)) {
			reverseAlts(e->alts);
			e++;
		}
	}
}

static void reverseSteps(Aword adr) {
	StepElem *e = (StepElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(StepElem));
		while (!endOfTable(e)) {
			reverseStms(e->exp);
			reverseStms(e->stm);
			e++;
		}
	}
}

static void reverseScrs(Aword adr) {
	ScrElem *e = (ScrElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ScrElem));
		while (!endOfTable(e)) {
			reverseStms(e->dscr);
			reverseSteps(e->steps);
			e++;
		}
	}
}

static void reverseActs(Aword adr) {
	ActElem *e = (ActElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ActElem));
		while (!endOfTable(e)) {
			reverseStms(e->nam);
			reverseTable(e->atrs, sizeof(AtrElem));
			reverseScrs(e->scradr);
			reverseVrbs(e->vrbs);
			reverseStms(e->dscr);
			e++;
		}
	}
}

static void reverseObjs(Aword adr, Boolean v2_5) {
	ObjElem *e = (ObjElem *) &memory[adr];
	ObjElem25 *e25 = (ObjElem25 *) &memory[adr];

	if (v2_5) {
		if (adr != 0 && !endOfTable(e25)) {
			reverseTable(adr, sizeof(ObjElem25));
			while (!endOfTable(e25)) {
				reverseTable(e25->atrs, sizeof(AtrElem));
				reverseVrbs(e25->vrbs);
				reverseStms(e25->dscr1);
				reverseStms(e25->dscr2);
				e25++;
			}
		}
	} else {
		if (adr != 0 && !endOfTable(e)) {
			reverseTable(adr, sizeof(ObjElem));
			while (!endOfTable(e)) {
				reverseTable(e->atrs, sizeof(AtrElem));
				reverseVrbs(e->vrbs);
				reverseStms(e->art);
				reverseStms(e->dscr1);
				reverseStms(e->dscr2);
				e++;
			}
		}
	}
}

static void reverseExts(Aword adr) {
	ExtElem *e = (ExtElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ExtElem));
		while (!endOfTable(e)) {
			if (!e->done) {
				reverseChks(e->checks);
				reverseStms(e->action);
			}
			e++;
		}
	}
}

static void reverseLocs(Aword adr) {
	LocElem *e = (LocElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(LocElem));
		while (!endOfTable(e)) {
			reverseStms(e->nams);
			reverseStms(e->dscr);
			reverseStms(e->does);
			reverseTable(e->atrs, sizeof(AtrElem));
			reverseExts(e->exts);
			reverseVrbs(e->vrbs);
			e++;
		}
	}
}

static void reverseClas(Aword adr) {
	ClaElem *e = (ClaElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ClaElem));
		while (!endOfTable(e)) {
			reverseStms(e->stms);
			e++;
		}
	}
	if (adr)
		reverse(&((Aword *)e)[1]);  /* The verb code is stored after the table */
}

static void reverseElms(Aword adr) {
	ElmElem *e = (ElmElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(ElmElem));
		while (!endOfTable(e)) {
			if (e->code == EOS) reverseClas(e->next);
			else reverseElms(e->next);
			e++;
		}
	}
}

static void reverseStxs(Aword adr) {
	StxElem *e = (StxElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(StxElem));
		while (!endOfTable(e)) {
			reverseElms(e->elms);
			e++;
		}
	}
}

static void reverseEvts(Aword adr) {
	EvtElem *e = (EvtElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(EvtElem));
		while (!endOfTable(e)) {
			reverseStms(e->code);
			e++;
		}
	}
}

static void reverseLims(Aword adr) {
	LimElem *e = (LimElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(LimElem));
		while (!endOfTable(e)) {
			reverseStms(e->stms);
			e++;
		}
	}
}

static void reverseCnts(Aword adr) {
	CntElem *e = (CntElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(CntElem));
		while (!endOfTable(e)) {
			reverseLims(e->lims);
			reverseStms(e->header);
			reverseStms(e->empty);
			reverseStms(e->nam);
			e++;
		}
	}
}

static void reverseRuls(Aword adr) {
	RulElem *e = (RulElem *) &memory[adr];

	if (adr != 0 && !endOfTable(e)) {
		reverseTable(adr, sizeof(RulElem));
		while (!endOfTable(e)) {
			reverseStms(e->exp);
			reverseStms(e->stms);
			e++;
		}
	}
}


/*----------------------------------------------------------------------

  reverseHdr()

  Reverse the header structure.

*/
void reverseHdr(AcdHdr *hdr) {
	// Reverse all words in the header except the first (version marking)
	for (uint i = 1; i < sizeof(AcdHdr) / sizeof(Aword); i++)
		reverse(&((Aword *)hdr)[i]);
}

/*----------------------------------------------------------------------

  reverseACD()

  Traverse all the data structures and reverse all integers.
  Only performed in architectures with reversed byte ordering, which
  makes the .ACD files fully compatible across architectures

  */
void reverseACD(Boolean v2_5) {
	reverseHdr(header);
	reverseWrds(header->dict);
	reverseTable(header->oatrs, sizeof(AtrElem));
	reverseTable(header->latrs, sizeof(AtrElem));
	reverseTable(header->aatrs, sizeof(AtrElem));
	reverseActs(header->acts);
	reverseObjs(header->objs, v2_5);
	reverseLocs(header->locs);
	reverseStxs(header->stxs);
	reverseVrbs(header->vrbs);
	reverseEvts(header->evts);
	reverseCnts(header->cnts);
	reverseRuls(header->ruls);
	reverseTable(header->init, sizeof(IniElem));
	reverseStms(header->start);
	reverseMsgs(header->msgs);

	reverseTable(header->scores, sizeof(Aword));
	reverseTable(header->freq, sizeof(Aword));
}

} // End of namespace Alan2
} // End of namespace Glk