//-----------------------------------------------------
extern class basic_sym_manager* get_hash_element(int index);
extern FILE *yyout;
extern "C"
{
	void printTail(int size, char *funcname)
	{
		fprintf(yyout, "func_%s_end:\n",funcname);
		fprintf(yyout, "\tmov\tesp,ebp\n");
		fprintf(yyout, "\tleave\n");		
		fprintf(yyout, "\tret\n");
	}
	void printHead(char *funcname, int size)
	{
		fprintf(yyout, "\tpush\tebp\n");
		fprintf(yyout, "\tmov\tebp, esp\n");
		
		if(!strcmp(funcname,"main"))
			fprintf(yyout, "\tsub\tesp, %d\n",size*4+64);
		else
			fprintf(yyout, "\tsub\tesp, %d\n",size*4+64);
			
	}
	void printENDP(char *funcname)
	{
		if(!strcmp(funcname,"main"))
		{
			fprintf(yyout, "Main ENDP\n");
		}
		else
			fprintf(yyout, "func_%s ENDP\n",funcname);
	}

	void codeGen()
	{
		int SymCnt=0;
		int i,j,k;
		basic_sym_manager *p_hash;		

		SymCnt = get_func_cnt();
		for( i=0; i<SymCnt; i++)
		{
			p_hash=get_hash_element(i);	
			if(!strcmp(p_hash->funcname,"main"))
				fprintf(yyout, "Main PROC\n",p_hash->funcname );
			else
				fprintf(yyout, "func_%s PROC\n",p_hash->funcname );
			
			printHead(p_hash->funcname,p_hash->nParam);
			
			// Get a Parameter Value	
			for(j=p_hash->nParam -1; j>=0; j--)
			{
				fprintf(yyout,"\tmov\teax, dword ptr [ebp+%d]\n", 4*(j+2));
				fprintf(yyout,"\tmov\tdword ptr [ebp-%d], eax\n",4*(j+1));
			}		

			// Generate Asm Code From Intermediate Code
			for(j=0; j<p_hash->code.size(); j++)
			{
				if(p_hash->code[j].OPCODE == LABEL)
				{
					fprintf(yyout,"%s:\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == CMP)
				{
					if(p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tpop\teax\n");
						fprintf(yyout,"\tcmp\teax, ecx\n");
					}
					else
					{
						if(p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if(p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						}			
						if(p_hash->code[j].OPR1_TYPE ==TTABLE)
						{
							fprintf(yyout, "\tpop\teax\n");
						}

						if(p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\tecx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if(p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tecx, %d\n",Num_get(p_hash->code[j].OPR2));
						}
						if(p_hash->code[j].OPR2_TYPE ==TTABLE)
						{
							fprintf(yyout, "\tpop\tecx\n");
						}
						
						fprintf(yyout,"\tcmp\teax, ecx\n");
					}

					//dword ptr [ebp-4]
				}
				else if(p_hash->code[j].OPCODE == JMP)
				{
					fprintf(yyout,"\tjmp\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JE)
				{
					fprintf(yyout,"\tje\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JGE)
				{
					fprintf(yyout,"\tjge\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JLE)
				{
					fprintf(yyout,"\tjle\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JG)
				{
					fprintf(yyout,"\tjg\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JL)
				{
					fprintf(yyout,"\tjl\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == JNE)
				{
					fprintf(yyout,"\tjne\t%s\n",p_hash->code[j].OPT);
				}
				else if(p_hash->code[j].OPCODE == INC)
				{
					fprintf(yyout,"\tinc\tdword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
				}
				else if(p_hash->code[j].OPCODE == DEC)
				{
					fprintf(yyout,"\tdec\tdword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
				}
				else if(p_hash->code[j].OPCODE == ASSIGN)
				{
					if( p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tmov\tdword ptr [ebp-%d], ecx\n",4*(p_hash->code[j].OPR1+1));
					}
					else if ( p_hash->code[j].OPR2_TYPE == TNUM )
					{
						fprintf(yyout,"\tmov\tdword ptr [ebp-%d], %d\n", 4*(p_hash->code[j].OPR1+1), Num_get(p_hash->code[j].OPR2) );
					}
					else if ( p_hash->code[j].OPR2_TYPE == TVAR )
					{
						fprintf(yyout,"\tmov\tecx, dword ptr[ebp-%d]\n", 4*(p_hash->code[j].OPR2+1));
						fprintf(yyout,"\tmov\tdword ptr [ebp-%d], ecx\n",4*(p_hash->code[j].OPR1+1));
					}
				}
				else if(p_hash->code[j].OPCODE == ADD)
				{
					if( p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tpop\teax\n");
						fprintf(yyout,"\tadd\teax,ecx\n");
						fprintf(yyout,"\tpush\teax\n");
					}
					else
					{
						if( p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						}
						if( p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if( p_hash->code[j].OPR1_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\teax\n");
						}	

						if( p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tecx, %d\n",Num_get(p_hash->code[j].OPR2));
						}
						if( p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\tecx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if( p_hash->code[j].OPR2_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\tecx\n");
						}	
						fprintf(yyout,"\tadd\teax,ecx\n");
						fprintf(yyout,"\tpush\teax\n");
					}	
				}
				else if(p_hash->code[j].OPCODE == SUB)
				{
					if( p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tpop\teax\n");
						fprintf(yyout,"\tsub\teax,ecx\n");
						fprintf(yyout,"\tpush\teax\n");
					}
					else
					{
						if( p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						}
						if( p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if( p_hash->code[j].OPR1_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\teax\n");
						}	

						if( p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tecx, %d\n",Num_get(p_hash->code[j].OPR2));
						}
						if( p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\tecx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if( p_hash->code[j].OPR2_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\tecx\n");
						}	
						fprintf(yyout,"\tsub\teax,ecx\n");
						fprintf(yyout,"\tpush\teax\n");
					}	

				}
				else if(p_hash->code[j].OPCODE == MUL)
				{
					if( p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tpop\teax\n");
						fprintf(yyout,"\timul\teax,ecx\n");
						fprintf(yyout,"\tpush\teax\n");
					}
					else
					{
						if( p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						
						}
						if( p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if( p_hash->code[j].OPR1_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\teax\n");
						}	

						if( p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tecx, %d\n",Num_get(p_hash->code[j].OPR2));
							//fprintf(yyout,"\timul\teax,eax,%d\n",Num_get(p_hash->code[j].OPR2));
							fprintf(yyout,"\timul\teax,ecx\n");
							fprintf(yyout,"\tpush\teax\n");
						}
						if( p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\timul\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
							fprintf(yyout,"\tpush\teax\n");
							//fprintf(yyout,"\tmov\tecx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if( p_hash->code[j].OPR2_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\tecx\n");
							fprintf(yyout,"\timul\teax,ecx\n");
							fprintf(yyout,"\tpush\teax\n");
						}	
					}	

				}
				else if(p_hash->code[j].OPCODE == DIV)
				{
					if( p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tmov\tedx, 0\n");
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tpop\tebx\n");
						fprintf(yyout,"\tcdq\n");
						fprintf(yyout,"\tidiv\tebx\n");
						fprintf(yyout,"\tpush\teax\n");
					}
					else
					{
						if( p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						}
						if( p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if( p_hash->code[j].OPR1_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\teax\n");
						}	

						if( p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tebx, %d\n",Num_get(p_hash->code[j].OPR2));
						}
						if( p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\tebx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if( p_hash->code[j].OPR2_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\tebx\n");
						}
						fprintf(yyout,"\tcdq\n");
						fprintf(yyout,"\tmov\tedx, 0\n");
						fprintf(yyout,"\tidiv\tebx\n");
						fprintf(yyout,"\tpush\teax\n");
					}						
				}
				else if(p_hash->code[j].OPCODE == MOD)
				{
					if( p_hash->code[j].OPR1_TYPE == TTABLE && p_hash->code[j].OPR2_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\tecx\n");
						fprintf(yyout,"\tcdq\n");
						fprintf(yyout,"\tpop\tebx\n");
						
						fprintf(yyout,"\tidiv\tebx\n");
						fprintf(yyout,"\tpush\tedx\n");
					}
					else
					{
						if( p_hash->code[j].OPR1_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
						}
						if( p_hash->code[j].OPR1_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
						}
						if( p_hash->code[j].OPR1_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\teax\n");
						}	

						if( p_hash->code[j].OPR2_TYPE == TNUM)
						{
							fprintf(yyout,"\tmov\tebx, %d\n",Num_get(p_hash->code[j].OPR2));
						}
						if( p_hash->code[j].OPR2_TYPE == TVAR)
						{
							fprintf(yyout,"\tmov\tebx, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR2+1));
						}
						if( p_hash->code[j].OPR2_TYPE == TTABLE)
						{
							fprintf(yyout,"\tpop\tebx\n");
						}
						fprintf(yyout,"\tcdq\n");	
						fprintf(yyout,"\tidiv\tebx\n");
						fprintf(yyout,"\tpush\tedx\n");
					}						

				}
				else if(p_hash->code[j].OPCODE == CALL)
				{
					int i;
					if(paramcode.size() == 0)
					{
						//fprintf(yyout,"\tpushad\n");
						fprintf(yyout,"\tcall\tfunc_%s\n",p_hash->code[j].OPT);
						//fprintf(yyout,"\tpopad\n");
					}
					else
					{
						for(i=paramcode.size()-1; i>=0; i--)
						{
							if(paramcode[i].OPR1_TYPE == TVAR)
							{
								fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(paramcode[i].OPR1+1));
								fprintf(yyout,"\tpush\teax\n");
							}
							else if(paramcode[i].OPR1_TYPE == TNUM)
							{
								fprintf(yyout,"\tmov\teax, %d\n",Num_get(paramcode[i].OPR1));
								fprintf(yyout,"\tpush\teax\n");
							}
						}
						fprintf(yyout,"\tcall\tfunc_%s\n",p_hash->code[j].OPT);
						fprintf(yyout,"\tadd\tesp, %d\n",paramcode.size()*4);
						paramcode.clear();
					}
				}
				else if(p_hash->code[j].OPCODE == PRINT)
				{
					int i;
					int litsize=0;
					if(paramcode.size() == 0)
					{
						fprintf(yyout,"\tcall\tfunc_%s\n",p_hash->code[j].OPT);
					}
					else
					{
						fprintf(yyout,"\tPrint\t");	
						for(i=0; i<paramcode.size(); i++)
						{
							if(paramcode[i].OPR1_TYPE == TVAR)
							{
								fprintf(yyout,"\t, dword ptr [ebp-%d]",4*(paramcode[i].OPR1+1));
							}
							else if(paramcode[i].OPR1_TYPE == TNUM)
							{
								fprintf(yyout,"\t,%d",Num_get(paramcode[i].OPR1));
							}
							else if(paramcode[i].OPR1_TYPE == TLITERAL)
							{
								//fprintf(yyout,"\tpush\toffset string %s\n",Lit_get(paramcode[i].OPR1));
								if(i>0)
									fprintf(yyout,"\t, %s",Lit_get(paramcode[i].OPR1));
								else
									fprintf(yyout,"%s",Lit_get(paramcode[i].OPR1));
								litsize+=strlen(Lit_get(paramcode[i].OPR1));
							}
						}
						fprintf(yyout,"\n");
						paramcode.clear();
					}
				}
				else if(p_hash->code[j].OPCODE == PARAM)
				{
					// Push in Memory Vector
					paramcode.push_back(p_hash->code[j] );
				}
				else if(p_hash->code[j].OPCODE == RET)	
				{
					if(p_hash->code[j].OPR1_TYPE == TVAR)
					{
						fprintf(yyout,"\tmov\teax, dword ptr [ebp-%d]\n",4*(p_hash->code[j].OPR1+1));
					}
					else if(p_hash->code[j].OPR1_TYPE == TNUM)
					{
						fprintf(yyout,"\tmov\teax, %d\n",Num_get(p_hash->code[j].OPR1));
					}
					else if(p_hash->code[j].OPR1_TYPE == TTABLE)
					{
						fprintf(yyout,"\tpop\teax\n");
					}
					fprintf(yyout, "\tjmp\tfunc_%s_end\n",p_hash->funcname);
				}
				else if(p_hash->code[j].OPCODE == FUNCGET)
				{
					fprintf(yyout,"\tmov\tdword ptr [ebp-%d], eax\n",4*(p_hash->code[j].OPR1+1));
				}
			}
			
			printTail(p_hash->nParam, p_hash->funcname );
			printENDP(p_hash->funcname);
		}	
	}
}

