/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2011,2012,2013  Free Software Foundation, Inc.
 *
 *  GRUB 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/term.h>
#include <grub/misc.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/dl.h>
#include <grub/time.h>
#include <grub/speaker.h>

GRUB_MOD_LICENSE ("GPLv3+");

#define BASE_TIME 250
#define DIH 1
#define DAH 3
#define END 0

static const char codes[0x80][6] =
  {
    ['0'] = { DAH, DAH, DAH, DAH, DAH, END },
    ['1'] = { DIH, DAH, DAH, DAH, DAH, END },
    ['2'] = { DIH, DIH, DAH, DAH, DAH, END },
    ['3'] = { DIH, DIH, DIH, DAH, DAH, END },
    ['4'] = { DIH, DIH, DIH, DIH, DAH, END },
    ['5'] = { DIH, DIH, DIH, DIH, DIH, END },
    ['6'] = { DAH, DIH, DIH, DIH, DIH, END },
    ['7'] = { DAH, DAH, DIH, DIH, DIH, END },
    ['8'] = { DAH, DAH, DAH, DIH, DIH, END },
    ['9'] = { DAH, DAH, DAH, DAH, DIH, END },
    ['a'] = { DIH, DAH, END },
    ['b'] = { DAH, DIH, DIH, DIH, END },
    ['c'] = { DAH, DIH, DAH, DIH, END },
    ['d'] = { DAH, DIH, DIH, END },
    ['e'] = { DIH, END },
    ['f'] = { DIH, DIH, DAH, DIH, END },
    ['g'] = { DAH, DAH, DIH, END },
    ['h'] = { DIH, DIH, DIH, DIH, END },
    ['i'] = { DIH, DIH, END },
    ['j'] = { DIH, DAH, DAH, DAH, END },
    ['k'] = { DAH, DIH, DAH, END },
    ['l'] = { DIH, DAH, DIH, DIH, END },
    ['m'] = { DAH, DAH, END },
    ['n'] = { DAH, DIH, END },
    ['o'] = { DAH, DAH, DAH, END },
    ['p'] = { DIH, DAH, DAH, DIH, END },
    ['q'] = { DAH, DAH, DIH, DAH, END },
    ['r'] = { DIH, DAH, DIH, END },
    ['s'] = { DIH, DIH, DIH, END },
    ['t'] = { DAH, END },
    ['u'] = { DIH, DIH, DAH, END },
    ['v'] = { DIH, DIH, DIH, DAH, END },
    ['w'] = { DIH, DAH, DAH, END },
    ['x'] = { DAH, DIH, DIH, DAH, END },
    ['y'] = { DAH, DIH, DAH, DAH, END },
    ['z'] = { DAH, DAH, DIH, DIH, END }
  };

static void
grub_audio_tone (int length)
{
  grub_speaker_beep_on (1000);
  grub_millisleep (length);
  grub_speaker_beep_off ();
}

static void
grub_audio_putchar (struct grub_term_output *term __attribute__ ((unused)),
		    const struct grub_unicode_glyph *c_in)
{
  grub_uint8_t c;
  int i;

  /* For now, do not try to use a surrogate pair.  */
  if (c_in->base > 0x7f)
    c = '?';
  else
    c = grub_tolower (c_in->base);
  for (i = 0; codes[c][i]; i++)
    {
      grub_audio_tone (codes[c][i] * BASE_TIME);
      grub_millisleep (BASE_TIME);
    }
  grub_millisleep (2 * BASE_TIME);
}


static int
dummy (void)
{
  return 0;
}

static struct grub_term_output grub_audio_term_output =
  {
   .name = "morse",
   .init = (void *) dummy,
   .fini = (void *) dummy,
   .putchar = grub_audio_putchar,
   .getwh = (void *) dummy,
   .getxy = (void *) dummy,
   .gotoxy = (void *) dummy,
   .cls = (void *) dummy,
   .setcolorstate = (void *) dummy,
   .setcursor = (void *) dummy,
   .flags = GRUB_TERM_CODE_TYPE_ASCII | GRUB_TERM_DUMB,
   .progress_update_divisor = GRUB_PROGRESS_NO_UPDATE
  };

GRUB_MOD_INIT (morse)
{
  grub_term_register_output ("audio", &grub_audio_term_output);
}

GRUB_MOD_FINI (morse)
{
  grub_term_unregister_output (&grub_audio_term_output);
}