/*
    Copyright 2004,2005,2006,2008,2009,2010 Luigi Auriemma

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

    http://www.gnu.org/licenses/gpl-2.0.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


#define VER         "0.2.1"



void morse(int chr);
void std_err(void);



int main(int argc, char *argv[]) {
    FILE    *fd;
    int     c;
    char    *fname;

    setbuf(stdout, NULL);

    fputs("\n"
        "Morse generator "VER"\n"
        "by Luigi Auriemma\n"
        "e-mail: aluigi@autistici.org\n"
        "web:    aluigi.org\n"
        "\n", stderr);

    if(argc < 2) {
        printf("\n"
            "Usage: %s <text_file/stdin>\n"
            "\n", argv[0]);
        exit(1);
    }
    fname = argv[1];

    if(!strcmp(fname, "-")) {
        fd = stdin;
    } else {
        fd = fopen(fname, "rb");
        if(!fd) std_err();
    }

    while((c = fgetc(fd)) >= 0) {
        morse(c);
    }

    if(fd != stdin) fclose(fd);
    printf("\n");
    return(0);
}



void morse(int chr) {
    static int  bol = 1;    // begin of line
    static const unsigned char strange_chars[] = {
                    ' ','E',' ',',','f',',','.','t',' ','^','%','S','<','E',' ','Z',
                    ' ',' ','`','`','"','"','.','-','-','~','`','S','>','e',' ','Z',
                    'Y','Y','i','c','e','o','Y','I','S','`','c','a','<','-','-','E',
                    '-','`','+','2','3','`','u','P','-',',','1','`','>','%','%','%',
                    '?','A','A','A','A','A','A','A','C','E','E','E','E','I','I','I',
                    'I','D','N','O','O','O','O','O','x','0','U','U','U','U','Y','D',
                    'B','a','a','a','a','a','a','e','c','e','e','e','e','i','i','i',
                    'i','o','n','o','o','o','o','o','+','o','u','u','u','u','y','b',
                    'y' };
    static const unsigned chr2morse[256] = {    // created with my buildmorsetab tool
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    3,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        3, 2662, 1625,    0, 9621,    0,  345, 1705,  422, 2470,  150,  409, 2650, 2390, 2457,  406,
      682,  681,  677,  661,  597,  341,  342,  346,  362,  426, 1386, 1638,    0,  598,    0, 1445,
     1641,    9,   86,  102,   22,    1,  101,   26,   85,    5,  169,   38,   89,   10,    6,   42,
      105,  154,   25,   21,    2,   37,  149,   41,  150,  166,   90,    0,    0,    0,    0, 2469,
        0,    9,   86,  102,   22,    1,  101,   26,   85,    5,  169,   38,   89,   10,    6,   42,
      105,  154,   25,   21,    2,   37,  149,   41,  150,  166,   90,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,  170,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
      617,    0,    0,    0,  153,  617,  153,  358,  601,  357,    0,    0,    0,    0,    0,    0,
      421,  666,    0,    0,    0,    0,  106,    0,  106,    0,    0,    0,  165,    0,  361,    0
    };
    unsigned    code = 0,
                t;
    int         i;

    // carriage return / line feed
    if(chr == '\r') return; // otherwise \r\n will be dumped as \r\r\n on Windows
    if((chr == '\n') || (chr == '\f') || (chr == '\v')) {
        fputc(chr, stdout);
        bol = 1;
        return;
    }

    // space before morse code
    if(bol) {
        bol = 0;
    } else {
        fputc(' ', stdout);
    }

    code = chr2morse[chr];
    if(!code) {
        // special chars, I handle them here for future implementations
        // most of them are already handled, anyway I leave them here
        switch(chr) {
            case '\a':  code = 131353;  break;  // bell handled like a warning
            case '\b':  code = 66069;   break;  // the alternative is .. ..
            case '\v':
            case '\f':  code = 131350;  break;  // separator
            case 'ä':
            case 'Ä':
            case 'æ':
            case 'Æ':   code = 537;     break;
            case 'à':
            case 'À':
            case 'å':
            case 'Å':   code = 131369;  break;
            case 'ç':
            case 'Ç':   code = 65830;   break;
            case 'š':   code = 554;     break;
            case 'ð':
            case 'Ð':   code = 66085;   break;
            case 'è':
            case 'È':   code = 131353;  break;
            case 'é':
            case 'É':   code = 65829;   break;
            case 'ñ':   code = 131610;  break;
            case 'ö':
            case 'Ö':
            case 'ø':
            case 'Ø':
            case 'ó':
            case 'Ó':   code = 298;     break;
            case 'Þ':
            case 'þ':   code = 65833;   break;
            case 'ü':
            case 'Ü':   code = 549;     break;
            default: break;
        }

        // crazy and chaotic non-english characters guesser... the last hope
        if(chr >= 0x7f) {
            if(chr <= (0xff - 0x20)) code = chr2morse[chr + 0x20];  // like Ü and ü
            if(!code) {
                chr = strange_chars[chr - 0x7f];    // use the english version
                t = chr2morse[chr];
                while(t) {
                    for(code = 1; code <= t; code <<= 2); code |= t; // add a .
                    for(i = 0; i < 256; i++) if(chr2morse[i] == code) break;
                    if(i >= 256) break;
                    for(code = 2; code <= t; code <<= 2); code |= t; // add a -
                    for(i = 0; i < 256; i++) if(chr2morse[i] == code) break;
                    if(i >= 256) break;
                    t = code;   // next char
                }
            }
        }
    }

    while(code) {
        switch(code & 3) {
            case 3:  fputc(' ', stdout); break; // check it as first
            case 1:  fputc('.', stdout); break; // do NOT use '·' because can't be showed in all the environments
            case 2:  fputc('-', stdout); break;
            default: fputc(' ', stdout); break;
        }
        code >>= 2;
    }
}



void std_err(void) {
    perror("\nError");
    exit(1);
}


