/*
    Module : Country

    Purpose: Operations to do with the 'country' array.

    Version: 0.05

    Date   : 5th June 2008

    Started: 11th July 2005

    Author : Rick Murray
             http://www.heyrick.co.uk/eurovision/

*/

#include "euroscore.h"




struct countrydef *country = NULL;
unsigned long countrycount = 0;  // how many we have right now
unsigned long countrymax = 0;    // how many we can have before reallocation required

int  scorelist[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12};




void country_extendarray(void);
void country_tidyup(void);
void country_process(void);
void country_outputeach(void);
void country_gentable(int which);
void country_votedforme(int which);
char *country_alias(int which, int title);
char *country_matchtoalias(char *ctry);




void country_extendarray(void)
{
   /* Creates/extends the 'country' array for additional entries */

   // first time? set tidyup handler
   if (countrymax == 0)
      atexit(country_tidyup);

   countrymax += 10; // extend array by ten entries at a time...

   country = memtrace_namedrealloc(country, (countrymax * sizeof(struct countrydef)), __func__);
   if (country == NULL)
   {
      fprintf(stderr, "Unable to (re)allocate memory for country array.\n\n");
      exit(EXIT_FAILURE);
   }

   return;
}



void country_tidyup(void)
{
   /* Delete array on exit... */

   if (country == NULL)
   {
      fprintf(stderr, "Country array not allocated or already freed!\n");
      return;
   }

   memtrace_free(country);
   country = NULL;

   return;
}



void country_process(void)
{
   /* Works out, from the scores, what 'position' the country would rank */

   struct scoredef
   {
      int  offset;
      int  score;
      signed int position;
   };
   struct scoredef *scores = NULL;
   int  recurse = 0;
   int  value = 0;
   int  loop = 0;
   int  count = 0;
   int  sortdone = FALSE;

   // Count how many actual scores we have
   for (recurse = 0; recurse < countrycount; recurse++)
      if (country[recurse].score >= 0)
         count++;

   // Allocate this much in our array
   scores = memtrace_calloc((count + 2), sizeof(struct scoredef));
   if (scores == NULL)
   {
      fprintf(stderr, "Unable to allocate memory for %d score definitions.\n\n", count);
      exit(EXIT_FAILURE);
   }

   // Copy across into the score array
   count = 0;
   for (recurse = 0; recurse < countrycount; recurse++)
   {
      if (country[recurse].score != -1)
      {
         count++; // pre-increment, as array 0 is sort buffer and we leave with 'correct' count
         scores[count].offset   = recurse;
         scores[count].score    = country[recurse].score;
         scores[count].position = 0;
      }
   }

   // Basic Boring Bubble Sort
   // This is a terrible sort, but it gets the job done and ... I've had a few glasses
   // of rosé wine while enjoying a good film on TV and frankly a crappy sorting routine
   // with no imagination is about right for my current state of mind.
   // [ I betcha you're glad you're reading this code now, huh? :-) ]
   //
   // The only thing to note about the sort routine is that it works BACKWARDS:
   // highest entry first, because the song with the most points is #1!
   do
   {
      sortdone = TRUE;

      for (recurse = count; recurse > 0; recurse--)
      {
         if ( scores[recurse].score < scores[(recurse + 1)].score )
         {
            scores[0] = scores[(recurse + 1)];
            scores[(recurse + 1)] = scores[recurse];
            scores[recurse] = scores[0];
            sortdone = FALSE;
         }
      }
   } while ( sortdone == FALSE );

   // Put the positions into the array (for dupe checking)
   for (recurse = 1; recurse <= count; recurse++)
      scores[recurse].position = recurse;

   // Now we must dupe-check
   for (recurse = 1; recurse <= count; recurse++)
   {
      int  isadupe = FALSE;
      
      // get a score
      value = scores[recurse].score;

      // check against every FOLLOWING entry
      for (loop = (recurse + 1); loop <= count; loop++)
      {
         // a match?
         if (scores[loop].score == value)
         {
            isadupe = TRUE; // so we know to dupify the original
            scores[loop].position = (0 - scores[recurse].position); // -ve
         }
      }

      // original needs fixing?
      if (isadupe == TRUE)
         scores[recurse].position = (0 - scores[recurse].position);
   }

   // now copy the positional information BACK to the main array
   for (recurse = 1; recurse <= count; recurse++)
      country[scores[recurse].offset].ranking = scores[recurse].position;

   // release memory, we're done!
   memtrace_free(scores);

   return;
}



void country_outputeach(void)
{
   /* Sort out what to output for each country */

   int loop = 0;

   for (loop = 0; loop < countrycount; loop++)
      country_gentable(loop);

   // I could have made it a two-line function:
   //    for (int loop = 0; loop < countrycount; loop++)
   //       country_gentable(loop);
   // but OpenWatcom doesn't support this extension to the C language
   // (the definition of a variable at first call - "for (INT loop = [etc]".
   // Oh well.
   // Acorn (Norcroft) C/C++ wins again! :-)

   return;
}



void country_gentable(int which)
{
   /* Generates the HTML for the table (for each country) */
   int  sloop = 0;
   double pop = 0;
   int  tblwid = 0;
   int  trswid = 0;
   struct imgsizedef *imgsize = NULL;

   fprintf(out, "<a name=\"#%s\"></a>\r\n", country[which].name);
   fprintf(out, "<table border=\"0\" width=\"100%%\">\r\n  <tr>\r\n");

   fprintf(out, "    <td colspan=\"3\"><big><big><big><b>%s</b></big></big> ", \
           country_alias(which, 1));
           
   if (country[which].score != -1)
   {
      int  myplace = 0;
      
      // we got a score!
      fprintf(out, "<font color=\"#2F2F4F\">[%d points", country[which].score);

      // the position
      if (country[which].ranking < 0)
      {
         // JOINT ranking!
         myplace = abs(country[which].ranking);
         fprintf(out, ", joint %d", myplace);
      }
      else
      {
         myplace = country[which].ranking;
         fprintf(out, ", %d", myplace);
      }

      // Append 'st', 'nd', 'rd', or 'th'. Allows up to 50 songs (God help Wogan in THAT case!)
      switch (myplace)
      {
         case  1 : /* Everything XXst */
         case 21 :
         case 31 : /* fall-throughs are intentional! */
         case 41 : fprintf(out, "<sup>st</sup>");
                   break;

         case  2 : /* Everything XXnd */
         case 22 :
         case 32 : /* fall-throughs are intentional! */
         case 42 : fprintf(out, "<sup>nd</sup>");
                   break;

         case  3 : /* Everything XXrd */
         case 23 :
         case 33 : /* fall-throughs are intentional! */
         case 43 : fprintf(out, "<sup>rd</sup>");
                   break;

         default : /* Everything else is XXth */
                   fprintf(out, "<sup>th</sup>");
                   break;
      }

      // Do we have a comment?
      if ( strlen(country[which].comment) > 0 )
         fprintf(out, " <font size=\"2\">(<i>%s</i>)</font>", country[which].comment);
   }
   else
   {
      // we don't got a score!
      fprintf(out, "<font color=\"#4F2F2F\">[n/a");
   }
   fprintf(out, "]</font></big></td>\r\n  </tr>\r\n");

 
   fprintf(out, "\
  <tr>\r\n\
    <td><strong>%s</strong></td>\r\n\
    <td><strong>Voted for:</strong></td>\r\n\
    <td><strong>Was given points by:</strong></td>\r\n\
  </tr>\r\n", country[which].song);

   // now we fart around with the sizes to build an appropriate table
   // this requires "previous knowledge"...
   switch(year)
   {
      // 360x254 4:3 aspect from Alyson's framegrabber; image source NDR/ARD1
      case 2004: tblwid = 360; // 50% of source
                 trswid = 25;
                 break;

      // 360x214 14:9 aspect from Alyson's framegrabber; image source BBC3/BBC1
      case 2005: tblwid = 360; // 50% of source
                 trswid = 25;
                 break;

      // 360x212 14:9 aspect from Alyson's framegrabber; image source NDR/ARD1
      case 2006: tblwid = 360; // 50% of source (note: semi-final was 360x213!)
                 trswid = 25;
                 break;

      // 384x212 16:9 aspect from RicksPC's capture card; image source BBC3/BBC1
      case 2007: tblwid = 384; // 50% of source
                 trswid = 25;
                 break;

      // 502x282 16:9 aspect from Aiko's capture card; image source BBC3/ BBC1
      case 2008: tblwid = 502; // 66% of source
                 trswid = 15;
                 break;

      // otherwise assume will follow 2008's trend
      default:   tblwid = 502;
                 trswid = 15;
                 break;
   }

   // sort out the image links
   if ( strlen(country[which].image) > 0 )
   {
      if (country[which].score == -1)
      {
         if (country[which].semi != 0)
         {
            // we have a numbered semi-final
            // note that it is '1' or '2' (as ASCII number, not decimal value)
            if (country[which].semi == '1')
            {
               // if '1', it is the first semi-final
               sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
                       SEMIPATHONE, country[which].image, SUFFIX);
            }
            else
            {
               // otherwise assume the second semi-final; apparently
               // the EBU limit is for 45 countries; and god help us
               // if we were to require more than two semi=finals!!!
               sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
                       SEMIPATHTWO, country[which].image, SUFFIX);
            }
         }
         else
         {
            // no numbered semi-final, so we'll do it the old way
            sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
                    SEMIPATHOLD, country[which].image, SUFFIX);
         }
      }
      else
      {
         // we have a final score, so we must be a finalist country
         // hence we take pictures from the grand final content.
         sprintf(workspace, "%s%d%s%s%s", PATHPREFIX, year,
                 FINALPATH, country[which].image, SUFFIX);
      }

      imgsize = imagesize_getimagesize(workspace);

      // we ALWAYS set table width to 360 pixels (should be image width, but...)
      fprintf(out, "  <tr>\r\n    <td width=\"%d\" valign=\"top\"><img src=\"%s\"",
              tblwid, workspace);
      fprintf(out, "    width=\"%d\" height=\"%d\" border=\"0\"></td>\r\n    <td valign=\"top\"><pre>\r\n", \
              imgsize->width, imgsize->height);
   }
   else
   {
      // no image; width is also 360 pixels. This assures consistency.
      fprintf(out, "  <tr>\r\n    <td width=\"%d\" valign=\"middle\" align=\"center\">\r\n",
              tblwid);
      fprintf(out, "      There is no image currently<br>\r\n      available for this entry.</td>\r\n");
      fprintf(out, "    <td valign=\"top\"><pre>\r\n");
   }

   for (sloop = 0; sloop < 10; sloop++)
   {
      int  val = scorelist[sloop];
      char nme[STRLEN] = "";

      strcpy(nme, country_matchtoalias(country[which].pointsto[val]));

      fprintf(out, "%s%2d - <a href=\"#%s\">%s</a>%s\r\n", \
                   ((val == 12) ? "<b>" : ""), \
                   val, country[which].pointsto[val], nme, \
                   ((val == 12) ? "</b>" : "") );
   }
   fprintf(out, "    </pre>\r\n    </td>\r\n");

   if (country[which].score == -1)
   {
      fprintf(out, "    <td width=\"%d%%\" valign=\"top\" align=\"center\">\r\n", trswid);
      // fprintf(out, "    <i>Nobody, they did not<br> pass the semi-final.</i>\r\n");
      fprintf(out, "    <i>Nobody, they did not pass the <nobr>semi-final</nobr>.</i>\r\n");
   }
   else
   {
      fprintf(out, "    <td width=\"%d%%\" valign=\"top\">\r\n", trswid);
      fprintf(out, "      <font size = \"-1\">\r\n");
      country_votedforme(which);
      // following on from previous statistics...

      // work out what this represents as a %ge of the total vote
      pop = ( ((double)country[which].score * (double)100) / (double)(popularity - 58) );
      fprintf(out, "      <i>The popularity of this song was %.1f%%.</i><br>\r\n", pop);
   }
   fprintf(out, "    </td>\r\n  </tr>\r\n</table>\r\n<p>&nbsp;<p>\r\n\r\n\r\n");

   return;
}



void country_votedforme(int which)
{
   /* Searches for every country that "voted for me", puts them into value order,
      then outputs the list as HTML... */

   // FOR NOW: Just outputs "voted for" raw and unsorted...
   // ASSUMES: country names are cased identically (i.e. won't match "Bosnia" with "bosnia")

   int  recurse = 0;
   int  loop = 0;
   int  accscore = 0;

   struct voteddef
   {
      char name[STRLEN];
      char ref[STRLEN];
      int  points;
   };
   struct voteddef *voted = NULL;
   struct voteddef vtmp[1];
   int  votedcount = 0;
   int  sortdone = 0;
   int  twelves = 0;

   voted = memtrace_calloc(countrycount, sizeof(struct voteddef));
   if (voted == NULL)
   {
      fprintf(stderr, "Unable to (re)allocate memory for who-voted-for-me array.\n\n");
      exit(EXIT_FAILURE);
   }

   // Get the 'score' given by each country [is safe, a country cannot vote for itself]
   for (recurse = 0; recurse < countrycount; recurse++)
   {
      // in each score...
      for (loop = 1; loop <= 12; loop++)
      {
         // I think doing a flat 1-12 is less hectic than doing 1...8,10,12
         // Entries 9 and 11 will (should!) be blank.

         if ( strcmp(country[which].name, country[recurse].pointsto[loop]) == 0 )
         {
            // We have a match!
            strcpy(voted[votedcount].name, country[recurse].name);
            strcpy(voted[votedcount].ref , country_matchtoalias(country[recurse].name));
            voted[votedcount].points     = loop;
            accscore += loop;
            votedcount++;
         }
      }
   }


   // Sort the score table; though bubble sorts are slow and clunky,
   // note that we only have a handful of entries to sort through...
   
   // First we sort ALPHABETICALLY by NAME
   do
   {
      sortdone = TRUE;

      for (recurse = 0; recurse < (votedcount - 1); recurse++)
      {
         if ( strcmp(voted[recurse].name, voted[(recurse + 1)].name) > 0 )
         {
            vtmp[0] = voted[recurse];
            voted[recurse] = voted[(recurse + 1)];
            voted[(recurse + 1)] = vtmp[0];
            sortdone = FALSE;
         }
      }
   } while ( sortdone == FALSE );

   // Then we sort NUMERICALLY by SCORE

   // This is so we can arrive at lists like:
   //   12 Bosnia
   //   12 Denmark
   //   12 France
   //   11 Germany
   //   [etc]

   do
   {
      sortdone = TRUE;

      for (recurse = (votedcount - 1); recurse >= 0; recurse--)
      {
         if ( voted[recurse].points < voted[(recurse + 1)].points )
         {
            vtmp[0] = voted[(recurse + 1)];
            voted[(recurse + 1)] = voted[recurse];
            voted[recurse] = vtmp[0];
            sortdone = FALSE;
         }
      }
   } while ( sortdone == FALSE );


   // Output the score table, and count number of '12' scores
   twelves = 0;
   for (recurse = 0; recurse < votedcount; recurse++)
   {
      // Output given score
      //
      // Is in "typewriter" style.
      // Always two digits wide, if less than ten it is space-padded.
      // If 12, it starts a 'bold' section.
      fprintf(out, "      %s<tt>%s%d</tt>",
                   ((voted[recurse].points == 12) ? "<b>"    : ""),
                   ((voted[recurse].points <  10) ? "&nbsp;" : ""),
                     voted[recurse].points);

      // Now linked country hame
      fprintf(out, " <a href=\"#%s\">%s</a>", voted[recurse].name, voted[recurse].ref);

      // Switch off bold if 12 points, and in any case force a linebreak...
      fprintf(out, "%s<br>\r\n", ((voted[recurse].points == 12) ? "</b>" : ""));

      if (voted[recurse].points == 12)
         twelves++;
   }

   // Statistics
   fprintf(out, "      </font>\r\n    </td>\r\n  </tr>\r\n\r\n");
   fprintf(out, "  <tr>\r\n    <td>&nbsp;</td>\r\n    <td colspan = 2>\r\n");
   fprintf(out, "      <i>%d countr%s voted for this song,</i><br>\r\n",
           votedcount, ((votedcount == 1) ? "y" : "ies") );
   if (twelves > 0)
      fprintf(out, "      <i>There %s %d top score%s,</i><br>\r\n",
              ((twelves == 1) ? "was" : "were"), twelves, ((twelves == 1) ? "" : "s") );
   // Popularity follows - but it is worked out in "country_gentable()"
   // We'll drop in a verification, useful for checking the data integrity (typos, etc)!
   fprintf(out, "      <!-- this country scored %d, our survey said... %d -->\r\n",
           country[which].score, accscore);

   // Release memory claim
   memtrace_free(voted);

   return;
}



char *country_alias(int which, int title)
{
   /* Returns:
        If 'title' != 0 and 'titlename' defined     : returns 'titlename'
        If 'title' != 0 and 'titlename' NOT defined : returns 'country' name
        If 'title' == 0 and 'refname' defined       : returns 'refname'
        If 'title' == 0 and 'refname' NOT defined   : returns 'country' name

      This is so we can just call Macedonia "Macedonia", and have the 'F.Y.R.' added.

      The title name is the name given in the country's title and it must always
      be complete, for example:
        Bosnia & Herzegovina

      The reference name is used elsewhere instead of the given country name,
      and can therefore be abbreviated slightly, for example:
        Bosnia & H.

      Therefore:
        Country        = "Bosnia"
        Title name     = "Bosnia &amp; Herzegovina"
        Reference name = "Bosnia &amp; H."

      IMPORTANT! No HTML munging is applied, so strings must be HTMLised
                 (in other words, you should write "&amp;" and not just "&").
   */

   if (title != 0)
   {
      if (strlen(country[which].titlename) > 0)
         return country[which].titlename;
      else
         return country[which].name;
   }
   else
   {
      if (strlen(country[which].refname) > 0)
         return country[which].refname;
      else
         return country[which].name;
   }
}



char *country_matchtoalias(char *ctry)
{
   /* Given a name, looks it up, then gets an alias (if any) */

   char src[STRLEN] = "";
   char dst[STRLEN] = "";
   int  loop = 0;
   int  recurse = 0;

   for (loop = 0; loop < strlen(ctry); loop++)
      src[loop] = tolower(ctry[loop]);

   for (recurse = 0; recurse < countrycount; recurse++)
   {
      for (loop = 0; loop < strlen(ctry); loop++)
         dst[loop] = tolower(country[recurse].name[loop]);

      if ( strcmp(src, dst) == 0 )
      {
         // matched!
         if (strlen(country[recurse].refname) > 0)
            return country[recurse].refname;
         else
            return country[recurse].name;
      }
   }

   // this happens if we fail to match; is 99.9% the cause
   // of a typo like "Cpyrus" or "Azerbajan" or somesuch...
   sprintf(dst, "{%s ?}", ctry);
   return dst;
}