/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 Daniel Stenberg
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "autoconf.h"

#include <stdio.h>
#include <pthread.h>

#include "kernel.h"
#include <sys/time.h>

#ifdef ROCKBOX_HAS_SIMSOUND
#include "sound.h"
#endif

long current_tick = 0;
extern void sim_tick_tasks(void);

static void msleep(int msec)
{
    struct timeval delay;
    
    delay.tv_sec = msec / 1000;
    delay.tv_usec = (msec % 1000) * 1000;
    select(0, NULL, NULL, NULL, &delay); /* portable sub-second sleep */
}

/*
 * This is not a target thread, so it does not fall under the 1 thread at a
 * time thing.
 */
static void update_tick_thread()
{
    struct timeval start, now;
    long new_tick;

    gettimeofday(&start, NULL);
    while (1)
    {
        msleep(5); /* check twice per simulated target tick */
        gettimeofday(&now, NULL);
        new_tick = (now.tv_sec - start.tv_sec) * HZ
                   + (now.tv_usec - start.tv_usec) / (1000000/HZ);
        if (new_tick > current_tick)
        {
            sim_tick_tasks();
            current_tick = new_tick;
        }
    }
}

/*
 * We emulate the target threads by using pthreads. We have a mutex that only
 * allows one thread at a time to execute. It forces each thread to yield()
 * for the other(s) to run.
 */

pthread_mutex_t mp;

void init_threads(void)
{  
    pthread_t tick_tid;

    pthread_mutex_init(&mp, NULL);
    /* get mutex to only allow one thread running at a time */
    pthread_mutex_lock(&mp);

    /* start a tick thread */
    pthread_create(&tick_tid, NULL, (void *(*)(void *)) update_tick_thread, 
                   NULL);

#ifdef ROCKBOX_HAS_SIMSOUND /* start thread that plays PCM data */
    {
        pthread_t sound_tid;
        pthread_create(&sound_tid, NULL,
                       (void *(*)(void *)) sound_playback_thread, 
                       NULL);
    }
#endif

}
/* 
   int pthread_create(pthread_t *new_thread_ID,
   const pthread_attr_t *attr,
   void * (*start_func)(void *), void *arg);
*/

void yield(void)
{
    pthread_mutex_unlock(&mp); /* return */
    msleep(1);                 /* prevent busy loop */
    pthread_mutex_lock(&mp);   /* get it again */
}

void newfunc(void (*func)(void))
{
    pthread_mutex_lock(&mp);
    func();
    pthread_mutex_unlock(&mp); 
}


int create_thread(void (*fp)(void), void* sp, int stk_size)
{
    pthread_t tid;
    int i;
    int error;

    /* we really don't care about these arguments */
    (void)sp;
    (void)stk_size;
    error = pthread_create(&tid,
                           NULL,   /* default attributes please */
                           (void *(*)(void *)) newfunc, /* function to start */
                           fp      /* start argument */);
    if(0 != error)
        fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
    else
        fprintf(stderr, "Thread %ld is running\n", (long)tid);

    yield();

    return error;
}

void sim_sleep(int ticks)
{
    pthread_mutex_unlock(&mp); /* return */
    msleep((1000/HZ) * ticks);
    pthread_mutex_lock(&mp);   /* get it again */
}

