/***********************************************************************

  Copyright by Telelogic AB 1998

  This Program is owned by Telelogic and is protected by national
  copyright laws and international copyright treaties. Telelogic grants
  you the right to use this Program on one computer or in one local
  computer network at any one time.

  Under this License you may only modify the source code for the purpose
  of adapting it to your environment. You must reproduce and include any
  copyright and trademark notices on all copies of the source code.  You
  may not use, copy, merge, modify or transfer the Program except as
  provided in this License.

  Telelogic does not warrant that the Program will meet your
  requirements or that the operation of the Program will be
  uninterrupted and error free. You are solely responsible that the
  selection of the Program and the modification of the source code will
  achieve your intended results and that the results are actually
  obtained.

************************************************************************/

#include "pmtool.h"

bool StringNeedsQuotes (const string &s) {
  for (unsigned i = 0; i < s.size(); i++)
    if (isspace(s[i]) || s[i] == '"' || s[i] == '\\')
      return true;
  return false;
}

string ExtractQuotedString (istream &is) {
  string s;
  is >> ws;

  bool quoted = false;
  int c;
  if ((c = is.get()) != EOF)
    if (c == '\"')
      quoted = true;
    else
      s += (char) c;
  
  while ((c = is.get()) != EOF) {
    if ((quoted && c == '\"') || (! quoted && isspace(c)))
      return s;
    if (quoted && c == '\\') {
      if ((c = is.get()) != EOF)
        s += char (c);
      else
        return s;
    }
    else
      s += char (c);
  }
  
  return s;
}

////////////////////////////////////////////////////////////
//  MessageUnpacker

void MessageUnpacker::UnpackReplyStatus (bool *success) {
  int status;
  UnpackInteger (&status);
  if (success) *success = (status == 0);
}

void MessageUnpacker::UnpackInteger (int *value) {
  if (error_) return;
  int val = 0;
  is_ >> val;

  if (value) *value = val;
}

void MessageUnpacker::UnpackBool (bool *value) {
  if (error_) return;
  int val;
  is_ >> val;

  if (value) *value=(bool)val;
}

void MessageUnpacker::UnpackString (string *value) {
  if (error_) return;
  string s;
  if (! GetToken(&s)) return;
  if (value) *value = s;
}


void MessageUnpacker::UnpackStringList (list<string> *strList)
{
  if (error_) return;
  int count = 0;

  UnpackInteger (&count);
  for (int i = 0; i < count; i++) {
    string str;
    UnpackString (&str);
    if (strList)
      strList->push_back (str);
  }
}

////////////////////////////////////////////////////////////
// PMConnection class

bool PMConnection::Open (string *status) {
  if (open_)
    return true;
  
  if (::SPInit(SET_EXTERN, "", NULL) == SPERROR) {
    if (status) {
      *status = "Could not connect to PostMaster: ";
      *status += SPErrorString(GetSPerrno());
    }
    return false;
  }
  open_ = true;
  return true;
}

void PMConnection::Close () {
  if (open_)
    ::SPExit();
  open_ = false;
}



////////////////////////////////////////////////////////////
// PMTool class


static bool CheckReply (const string &reply) {
  istrstream is((char *)reply.c_str());
  int statusCode;
  is >> statusCode;
  return statusCode == 0;
}

static string GetRPCErrorText(const string &reply) {
  istrstream is((char *)reply.c_str());
  int statusCode;
  is >> statusCode;

  string error;
  switch (statusCode) {
  case 0: return "RPC completed successfully";
  case 1: error += "server is busy: "; break;
  case 2: error += ""; break;
  case 3: error += "error code: "; break;
  case 4: error += ""; break;
  };

  error += ExtractQuotedString(is);
  return error;
}


bool PMTool::RPCStart () {
  MessagePacker packer;
  packer.PackInteger (toolType_);
  string data = packer.GetMessage();
  
  if (::SPBroadcast(SESTART,
                    (void *) data.c_str(),
                    data.size()) == SPERROR)
  {
    reply_ = "4 \"Could not send ";
    reply_ += "message"; // SPConvert (message);
    reply_ += ": ";
    reply_ += SPErrorString(GetSPerrno());
    reply_ +="\"";
    return false;
  }

  int pid;
  int replyMessage;
  void *replyData;
  int replyLength;
  do
    if (::SPRead(30000,
                 &pid,
                 &replyMessage,
                 &replyData,
                 &replyLength) == SPERROR)
    {
      reply_ = "4 \"Read failed: ";
      reply_ += SPErrorString(GetSPerrno());
      reply_ +="\"";
      return false;
    }
  while (! SP_MATCHREPLY(SESTART, replyMessage));

  reply_ = (const char *) replyData;
  SPFree (replyData);

  if (! CheckReply (reply_))
    return false;
  
  return true;
}


bool PMTool::RPCSendMessage (int message, const string &data, string *result) {
  if (::SPSendToTool(GetToolType(),
                     message,
                     (void *) data.c_str(),
                     data.size()) == SPERROR)
  {
    reply_ = "4 \"Could not send ";
    reply_ += "message"; // SPConvert (message);
    reply_ += ": ";
    reply_ += SPErrorString(GetSPerrno());
    reply_ +="\"";
    return false;
  }

  int pid;
  int replyMessage;
  void *replyData;
  int replyLength;
  do
    if (::SPRead(SPWAITFOREVER,
                 &pid,
                 &replyMessage,
                 &replyData,
                 &replyLength) == SPERROR)
    {
      reply_ = "4 \"Read failed: ";
      reply_ += SPErrorString(GetSPerrno());
      reply_ +="\"";
      return false;
    }
  while (! SP_MATCHREPLY(message, replyMessage));

  reply_ = (const char *) replyData;
  if (result) *result = reply_;
  SPFree (replyData);
  
  if (! CheckReply (reply_))
    return false;
  
  return true;
}


string PMTool::GetErrorText() const {
  string error = GetToolName();
  error += " ";
  error += GetRPCErrorText (reply_);
  return error;
}



////////////////////////////////////////////////////////////
//  TextEditorTool

bool PMEditorTool::RPCLoad (const string &filename, int *bufferId) {
  MessagePacker packer;
  packer.PackString (filename);
  string reply;
  if (! RPCSendMessage (SELOAD, packer.GetMessage(), &reply))
    return false;
  MessageUnpacker unpacker(reply);
  unpacker.UnpackReplyStatus();
  unpacker.UnpackInteger(bufferId);
  return unpacker.GetUnpackStatus();
}

bool PMEditorTool::RPCLoadCopy (const string &filename, int *bufferId) {
  MessagePacker packer;
  packer.PackString (filename);
  string reply;
  if (! RPCSendMessage (6105, packer.GetMessage(), &reply))
    return false;
  MessageUnpacker unpacker(reply);
  unpacker.UnpackReplyStatus();
  unpacker.UnpackInteger(bufferId);
  return unpacker.GetUnpackStatus();
}

bool PMEditorTool::RPCShow (int bufferId, const string &page) {
  MessagePacker packer;
  packer.PackInteger (bufferId);
  packer.PackString (page);  // pageName
  string reply;
  if (! RPCSendMessage (SESHOW, packer.GetMessage(), &reply))
    return false;
  MessageUnpacker unpacker(reply);
  unpacker.UnpackReplyStatus();
  return unpacker.GetUnpackStatus();
}

// end of file

