/* proc_rc.c - 예약 명령어들을 적절히 배분하여 처리하는 함수들을 호출해 주는 파일.
   Copyright (C) 2006 Weongyo Jeong (weongyo@gmail.com)

This file is part of ROVM.

ROVM 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, or (at your option) any later
version.

ROVM 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 GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include <stdio.h>

#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "clstree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "utils.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"
#include "ticket.h"

#include "log.h"

/**
   Request 에 대한 unique key (ticket) 를 발행한다.  이에 대한 결과값은
   TBUF 에 들어가야 한다.  또한 등록된 request 에 대한 처리 준비를 해야
   한다.

   @param r	Request 구조체.
   @return	발급된 ticket 버퍼의 포인터를 반환한다.
 */
char *
proc_rc_req (r)
     request_rec *r;
{
  char *tbuf;

  tbuf = create_ticketid (r);
  if (!tbuf)
    {
      request_add_error (r, ERRLOG_ERR, ERRLOG_MARK, "Failed to create Ticket ID.");
      return NULL;
    }

  /* 생성된 tbuf 를 등록한다.  */
  if (register_ticketid (r, tbuf))
    {
      request_add_error (r, ERRLOG_ERR, ERRLOG_MARK, "Failed to register Ticket ID.");
      rc_free (tbuf);
      return NULL;
    }

  return tbuf;
}

/**
   사용이 끝난 Ticket ID 에 대해서 그와 관련된 모든 자원들을 해제한다.

   @param r     Request 구조체
   @param tid   해제를 실시할 Ticket ID
   @return      정상 종료시 0, 그렇지 않을 경우 -1 과 함께 error 메세지.
 */
int
proc_rc_reqend (r, tid)
     request_rec *r;
     char *tid;
{
  if (destroy_tickets (REQUEST_ROVM (r), tid))
    return -1;

  return 0;
}

/**
   Request 로 들어온 opcode 를 처리한다.  만약 수행하는 동안 아무런 문제가
   없을 경우, COMMAND_OK 를 반환하게 된다.

   @param r      Request 구조체
   @param tid    Request 에 의해 전달된 ticket id
   @param op     Opcode 들이 들어있는 버퍼
   @param oplen  Opcode 길이
   @return       만약 성공적으로 수행이 완료되었다면 TICKET 변수를 사용하여
                 가져온 TK 을 반환한다.  이렇게 하는 이유는 이 함수를 호출한
                 곳에서 opcode 를 실행하고 난 후의 return 코드를 다룰 수
                 있도록 배려하기 위해서 이다.
 */
struct ticket *
proc_rc_opcode (r, tid, op, oplen)
     request_rec *r;
     char *tid;
     rc_opcode_t *op;
     rc_size_t oplen;
{
  rc_status_t rv;
  struct ticket *tk;

  tk = lookup_ticket (r, tid);
  if (!tk)
    {
      request_add_error (r, ERRLOG_INFO, ERRLOG_MARK, "Invalid Ticket ID");
      return NULL;
    }

  if (TICKET_STATUS (tk) != TICKET_IS_RELAX)
    {
      request_add_error (r, ERRLOG_INFO, ERRLOG_MARK, " Ticket ID is busy %d", TICKET_STATUS (tk));
      return NULL;
    }

  if (ticket_pre_config (tk))
    return NULL;

  rv = rovmcore_main (r, tk, op, oplen);
  if (rv)
    {
      ticket_post_config (tk);

      return NULL;
    }

  return tk;
}

/**
   사용자가 TID 에 대한 스택 상태를 요청하였을 때, 사용되는 함수입니다.  현재 스택
   상태를 Command `ERROR' 에 담아 사용자에게 보냅니다.  이 함수는 디버깅을 위한
   용이라는 것을 알기 바랍니다.

   @param r     Request 구조체
   @param tid   사용자가 보낸 TID
 */
void
proc_rc_getstack (r, tid)
     request_rec *r;
     char *tid;
{
  int idx = 0;
  rovm_stack_t *p;
  struct ticket *tk;

  tk = lookup_ticket (r, tid);
  if (!tk)
    {
      request_add_error (r, ERRLOG_INFO, ERRLOG_MARK, "Invalid Ticket ID");
      return;
    }

  p = TICKET_STACK (tk);
  while (p <= TICKET_SP (tk))
    {
      switch (STACK_TYPE (p))
        {
        case STACK_TYPE_CHAR:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d CHAR 0x%x", idx, GET_STACK_VALUE (CHAR, p));
          break;
        case STACK_TYPE_SHORT:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d SHORT 0x%x", idx, GET_STACK_VALUE (SHORT, p));
          break;
        case STACK_TYPE_INT:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d INT 0x%x", idx, GET_STACK_VALUE (INT, p));
          break;
        case STACK_TYPE_FLOAT:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d FLOAT %f", idx, GET_STACK_VALUE (FLOAT, p));
          break;
        case STACK_TYPE_DOUBLE:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d DOUBLE %f", idx, GET_STACK_VALUE (DOUBLE, p));
          break;
        case STACK_TYPE_OBJREF:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d OBJREF %p", idx, GET_STACK_VALUE (ADDR, p));
          break;
        case STACK_TYPE_STRINGREF:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d STRINGREF %p", idx, GET_STACK_VALUE (ADDR, p));
          break;
        case STACK_TYPE_ARRAYREF:
          request_add_error (r, ERRLOG_DEBUG, ERRLOG_MARK, "#%d ARRAYREF %p", idx, GET_STACK_VALUE (ADDR, p));
          break;
        default:
          rovm_rlog (r, ROVMLOG_WARNING, ROVMLOG_MARK,
                     "Can't handle stack type.");
        }

      idx += 1;
      p += 1;
    }
}

/**
   Remote Command 가 무엇인지 알수 없을 경우에 호출되며, 구조체 R 에 
   어떤 오류가 발생하였는지 기록한다.

   @param r	Request 구조체.
   @param rc	오류가 발생한 Remote Command.
 */
void
proc_rc_unknown (r, rc)
     request_rec *r;
     char rc;
{
  request_add_error (r, ERRLOG_ERR, ERRLOG_MARK, "Invalid Remote Command : rc = 0x%x", rc);
}
