/* Copyright (C) 2008, All rights reserved.
 *
 * Author: Chaoji Li<lichaoji@gmail.com>
 * Date: May 27, 2008
 *
 * http://www.litchie.net
 */

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <ctype.h>
#include <sapi.h>

#define MAX_WORD 4096

void usage()
{
    printf("speak -l                     list all available voices\n");
    printf("speak -q -l                  list all voices quietly\n");
    printf("speak <sentence>             speak the sentence out loud\n"); 
    printf("speak -<digit> <sentence>    speak the sentence using <digit>th voice\n");
    printf("speak -h                     print this message\n");
    printf("speak -f <file>              speak the content of file\n");
}

int main(int argc, char* argv[])
{
    HRESULT hr = S_OK;
    ISpObjectToken* cpVoiceToken = 0;
    IEnumSpObjectTokens* cpEnum = 0;
    ISpVoice* cpVoice = 0;
    ULONG ulCount = 0;
    ISpObjectTokenCategory *cpCategory = 0;
    wchar_t person[32] = L"";
    wchar_t word[MAX_WORD];
    int opt_list = 0;
    int opt_index = 0;
    int opt_quiet = 0;

    if (argc < 2)
    {
        usage();
        wcscpy(word, L"Give me your word.");
    }
    else
    {
        int i = 1;
        int j = 0;

        for (; i < argc; i++)
        {
            char *p = argv[i];
            if (strcmp(p, "-l")==0)
            {
                opt_list = 1;
            }
            else if (strcmp(p, "-h") == 0)
            {
                usage();
                wcscpy(word, L"Isn't it simple?");
            }
            else if (strcmp(p, "-f")==0)
            {
                if (++i == argc)
                {
                    fprintf(stderr, "-f: missing a file argument\n");
                    goto Error;
                }
                else
                {
                    char *filename = argv[i];
                    FILE *fp = fopen(filename, "r");
                    if (fp != NULL)
                    {
                        char buf[MAX_WORD];
                        int nread = 0;
                        nread = fread(buf, 1, MAX_WORD-1, fp);
                        buf[nread] = '\0';
                        int nconv = MultiByteToWideChar(
                              CP_ACP, 
                              0,         
                              buf, 
                              strlen(buf),       
                              &word[0],  
                              MAX_WORD - 1       
                            );
                        word[nconv] = 0;
                        fclose(fp);
                    }
                    break;
                }
            }
            else if (strcmp(p, "-q")==0)
            {
                opt_quiet = 1;
            }
            else if (p[0] == '-' && isdigit(p[1]))
            {
                opt_index = p[1] - '0';
            }
            else
            {                
                int n =  MultiByteToWideChar(
                      CP_ACP, 
                      0,         
                      p, 
                      strlen(p),       
                      &word[j],  
                      MAX_WORD - j - 2       
                    );
                j += n;
                word[j++] = L' ';
                word[j] = 0;
            }
        }        
    }

    hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        fprintf(stderr, "speak: cannot initialize COM\n");
        goto Error;
    }

    hr = CoCreateInstance(
        CLSID_SpVoice, 
        NULL, 
        CLSCTX_ALL, 
        IID_ISpVoice, 
        (void **)&cpVoice);
    if (FAILED(hr)) 
    {
        fprintf(stderr, "speak: no voice\n");
        goto Error;
    }
    
    hr = CoCreateInstance(
        CLSID_SpObjectTokenCategory, 
        NULL, 
        CLSCTX_ALL, 
        IID_ISpObjectTokenCategory, 
        (void**)&cpCategory);
    if (FAILED(hr) && opt_list) 
    {
        fprintf(stderr, "speak: cannot enumerate voices\n");
        goto Error;
    }
    else
    {
        cpCategory->SetId(SPCAT_VOICES, 0);
        cpCategory->EnumTokens(NULL,NULL, &cpEnum);
        if (cpEnum != 0)
        {
            hr = cpEnum->GetCount(&ulCount);
        }
    }

    int index = 0;

    while (cpEnum != 0 && ulCount--)
    {
       index++;       
       if (cpVoiceToken!=0)  
       {
           cpVoiceToken->Release();
           cpVoiceToken = 0;
       }

       hr = cpEnum->Next(1, &cpVoiceToken, NULL);
       if (FAILED(hr))
       {
            break;
       }

       wchar_t * desc = 0;
       cpVoiceToken->GetStringValue(NULL, &desc);
       if (opt_list && desc != 0)
       {
           wsprintf(word, L"Hello, I am %s", desc);
           wprintf(L"%d: %s\n", index, desc);
           hr = cpVoice->SetVoice(cpVoiceToken);
           if (SUCCEEDED (hr) && !opt_quiet)
           {
              hr = cpVoice->Speak(word, SPF_DEFAULT, NULL );
           }
       }
       else if (opt_index == index)
       {
            hr = cpVoice->SetVoice(cpVoiceToken);
            break;
       }   
    }

    if (opt_index > 0 && opt_index != index)
    {
        fprintf(stderr, "can't use %dth voice, use default voice\n", opt_index);
    }

    if (!opt_list && cpVoice != 0)
    {
        cpVoice->Speak(word, SPF_DEFAULT, NULL);
    }

Error:

    if (cpCategory != 0) 
    {
        cpCategory->Release();
    }

    if (cpEnum != 0) 
    {
        cpEnum->Release();
    }
    
    if (cpVoice != 0) 
    {
        cpVoice->Release();
    }
    
    if (cpVoiceToken != 0) 
    {
        cpVoiceToken->Release();
    }

    CoUninitialize();

    return (SUCCEEDED(hr) ? 0 : -1);
}