package kha.netsync;

import haxe.io.Bytes;
import haxe.macro.Context;
import haxe.macro.Expr;

class EntityBuilder {
	public static var nextId: Int = 0;

	macro static public function build(): Array<Field> {
		var fields = Context.getBuildFields();

		var isBaseEntity = false;
		for (i in Context.getLocalClass().get().interfaces) {
			var intf = i.t.get();
			if (intf.module == "kha.netsync.Entity") {
				isBaseEntity = true;
				break;
			}
		}

		var receive = macro {};

		var send = macro {};

		if (!isBaseEntity) {
			receive = macro {
				offset += super._receive(offset, bytes);
			};

			send = macro {
				offset += super._send(offset, bytes);
			};
		}

		var index: Int = 0;
		for (field in fields) {
			var replicated = false;
			for (meta in field.meta) {
				if (meta.name == "replicate" || meta.name == "replicated") {
					replicated = true;
					break;
				}
			}
			if (!replicated)
				continue;

			switch (field.kind) {
				case FVar(t, e):
					var fieldname = field.name;
					switch (t) {
						case TPath(p):
							switch (p.name) {
								case "Int":
									send = macro {
										$send;
										bytes.setInt32(offset + $v{index}, this.$fieldname);
									};
									receive = macro {
										$receive;
										this.$fieldname = bytes.getInt32(offset + $v{index});
									};
									index += 4;
								case "Float":
									send = macro {
										$send;
										bytes.setDouble(offset + $v{index}, this.$fieldname);
									};
									receive = macro {
										$receive;
										this.$fieldname = bytes.getDouble(offset + $v{index});
									};
									index += 8;
								case "Bool":
									send = macro {
										$send;
										bytes.set(offset + $v{index}, this.$fieldname ? 1 : 0);
									};
									receive = macro {
										$receive;
										this.$fieldname = bytes.get(offset + $v{index}) == 1 ? true : false;
									};
									index += 1;
							}
						default:
					}
				default:
			}
		}

		send = macro {
			$send;
			return $v{index};
		};

		receive = macro {
			$receive;
			return $v{index};
		};

		fields.push({
			name: "_send",
			doc: null,
			meta: [],
			access: isBaseEntity ? [APublic] : [APublic, AOverride],
			kind: FFun({
				ret: Context.toComplexType(Context.getType("Int")),
				params: null,
				expr: send,
				args: [
					{
						value: null,
						type: Context.toComplexType(Context.getType("Int")),
						opt: null,
						name: "offset"
					},
					{
						value: null,
						type: Context.toComplexType(Context.getType("haxe.io.Bytes")),
						opt: null,
						name: "bytes"
					}
				]
			}),
			pos: Context.currentPos()
		});

		fields.push({
			name: "_receive",
			doc: null,
			meta: [],
			access: isBaseEntity ? [APublic] : [APublic, AOverride],
			kind: FFun({
				ret: Context.toComplexType(Context.getType("Int")),
				params: null,
				expr: receive,
				args: [
					{
						value: null,
						type: Context.toComplexType(Context.getType("Int")),
						opt: null,
						name: "offset"
					},
					{
						value: null,
						type: Context.toComplexType(Context.getType("haxe.io.Bytes")),
						opt: null,
						name: "bytes"
					}
				]
			}),
			pos: Context.currentPos()
		});

		fields.push({
			name: "_id",
			doc: null,
			meta: [],
			access: isBaseEntity ? [APublic] : [APublic, AOverride],
			kind: FFun({
				ret: Context.toComplexType(Context.getType("Int")),
				params: null,
				expr: macro {return __id;},
				args: []
			}),
			pos: Context.currentPos()
		});

		var size = macro {
			return $v{index};
		};

		if (!isBaseEntity) {
			size = macro {
				return super._size() + $v{index};
			};
		}

		fields.push({
			name: "_size",
			doc: null,
			meta: [],
			access: isBaseEntity ? [APublic] : [APublic, AOverride],
			kind: FFun({
				ret: Context.toComplexType(Context.getType("Int")),
				params: null,
				expr: size,
				args: []
			}),
			pos: Context.currentPos()
		});

		if (isBaseEntity) {
			fields.push({
				name: "__id",
				doc: null,
				meta: [],
				access: [APublic],
				kind: FVar(macro : Int, macro kha.netsync.EntityBuilder.nextId++),
				pos: Context.currentPos()
			});
		}

		return fields;
	}
}