// This may look like C code, but it is really -*- C++ -*-

/***********************************************************
Copyright 1991 by the Massachusetts Institute of Technology

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission.  M.I.T. makes no representations about the
suitability of this software for any purpose.  It is provided "as is"
without express or implied warranty.


Author: Roman J. Budzianowski - Project Athena MIT

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

#include "ObjectView.h"
#include "ObjectEditor.h"
#include "EurekaObject.h"

#include "iostream.h"
#include <X11/StringDefs.h>

#include "WindowStream.h"

// translations are specified in the resource file

static void ObjectConnection(Core* w, XtPointer, XtPointer callData)
{
   Anchor* anchor = (Anchor*)w;
   GripCallData grip_data = (GripCallData)callData;
   char action_type = *grip_data->params[0];

   if (grip_data->num_params == 0)
     cerr <<  "IOAnchor: no action specified\n";

   switch(action_type) {
    case 'C':			// connect
//      cerr << "Anchor: connecting\n";
      anchor->StartConnection();
      break;
    case 'D':			// done
//      cerr << "Anchor: commiting connection!!\n";
      anchor->CommitConnection();
      break;
    case 'S':			// start moving anchor
//      cerr << "Anchor: start moving\n";
      break;
    case 'M':			// move anchor
//      cerr << "Anchor: moving\n";
      break;
    default:
      cerr << "Anchor: Unknown action\n";
   }
}

Anchor::Anchor(ObjectView* host, IO* io, Type type, Position offset, Location loc)
     : host(host), type(type), connected(NULL), io(io),
       GripWidget(Unmanaged,type==Input ? "input" : "output", host->Parent())
{
   SetValues(XtNborderWidth, (XtArgVal) 1);
   AddCallback(XtNcallback, ObjectConnection, (XtPointer)NULL);

//   if(type == Input)
//     SetValues(XtNforeground, (XtArgVal)border_pixel); // XXX doesn't work for Grip? No:
// border_pixel and background_pixel are already equal at this time
//   else if(type == Output)
//     SetValues(XtNforeground, (XtArgVal)background_pixel); // XXX doesn't work for Grip?

   PositionItself(offset,loc);
   Manage();
}

void Anchor::StartConnection()
{
   if(type == Output && !connected) {
      ((ObjectEditor*)Parent())->StartConnection(host,this);
   }
   else
     XBell(XtDisplay(), 50);
}

void Anchor::CommitConnection()
{
   if(type == Input && !connected){  
      ((ObjectEditor*)Parent())->CommitConnection(host,this);
   }
   else {
      XBell(XtDisplay(), 50);
//      XSync(XtDisplay(),True);	// XXX for debugging
      ((ObjectEditor*)Parent())->AbortConnection();
   }
}

void Anchor::UpdatePosition(Position offset, Location loc)
{
   switch(loc) {
    case Left:
      xoffset = -width;
      yoffset = offset - height/2;
      break;
    case Right:
      xoffset = host->Width();
      yoffset = offset - height/2;
      break;
    case Top:
      xoffset = -height;
      yoffset = offset - width/2;
      break;
    case Bottom:
      xoffset = offset - width/2;
      yoffset = host->Height();
      break;
    case Same:
    default:
      cerr << "Anchor: unknown type of location\n";
   }
}

void Anchor::PositionItself()
{
   // now we assume that the  parent accepts all geometry requests
   
   SetValues(XtNx,(XtArgVal)(host->x + xoffset),
	     XtNy,(XtArgVal)(host->y + yoffset));
}

void Anchor::PositionItself(Position offset, Location loc)
{
   UpdatePosition(offset,loc);
   PositionItself();
}

//
// ObjectView class
//

static void Manager(Core* , XEvent*, String* , Cardinal* );

static char defTranslations[] = 
" ~Ctrl <Btn3Down>:		ObjectViewManager(Move) \n\
  Ctrl <Btn1Down>:       ObjectViewManager(CreateWidget)";

void ObjectViewBindings(wstream& s)
{
   s << "Object Bindings:\n\n";
   s << (char*)defTranslations << "\n\n";
}

struct ObjectView_resources {
   XtTranslations translations;
};

#define XtNobjectViewTranslations "objectViewTranslations"

typedef ObjectView_resources *resource_pointer;

static XtActionsRec actions[] =
{ 
   {"ObjectViewManager", Manager}
};

#ifdef cfront_not_broken
#define offset(field) XtOffset(resource_pointer, field)
#else
#define offset(field) 0
#endif

static XtResource subresources[] = {
   {XtNobjectViewTranslations, XtCTranslations, XtRTranslationTable,
         sizeof(XtTranslations),
    offset(translations), XtRString, (caddr_t)defTranslations}
};

#undef offset

static void Manager(Core* c, XEvent*, String* args, Cardinal*)
{
   ObjectView *ov = (ObjectView *)c;
   char action_type = *args[0];
   switch(action_type) {
    case 'M':			// moving object
      ov->StartMoving();
//      cerr << "moving: " << ov->Name() << "\n";
      break;
    case 'C':
//      cerr << "create widget: " << ov->Name() << "\n";
      ov->CreateWidgetObject();
      break;
    default:
      cerr << "ObjectViewManager: unknown action\n";
   }
}

Boolean ObjectView::class_inited = False;

ObjectView::ObjectView(Widget workspace, String name, BaseObject* object,
		       Cardinal input_count, Cardinal output_count)
: CommandWidget(Unmanaged,name,workspace), inputs(NULL), outputs(NULL),
  input_count(input_count), output_count(output_count), object(object),
  input_off(0), output_off(0)
{
   InitializeClass();

   ObjectView_resources res;

//   cerr << "ObjectView: getting resources for:" << Name() << "\n";

//   XSync(XtDisplay(),False);	// debugging
   GetSubresources((caddr_t)&res, (String)Name(), "ObjectView", 
//   GetSubresources((caddr_t)&res, (String)NULL, (String)NULL,
		   subresources, XtNumber(subresources));

   // XXX
   // this is temporary: eventually both default_translations and user specified
   // translations will be loaded in sequence.
   //   tm.translations = res.translations; // ??? cfront doesn't like it

//   cerr << "ObjectView: setting translations.\n";

   SetValues(XtNtranslations,(XtArgVal)res.translations);


}

void ObjectView::InitializeClass()
{
   if(class_inited == False){

#ifndef cfront_not_broken
#include "fixres.h"
      int i=0;
#define offset(field) XtOffset(resource_pointer, field)
      fix_resource(i,offset(translations));i++;
#undef offset
#endif

      GetAppContext()->AddActions(actions, XtNumber(actions));
      class_inited = True;
   }
}

// have to change io positioning to dynamic XXX
#define ANCHOR_HEIGHT 8

void ObjectView::AddInput(IO* io)
{
   if(!input_off){
      int needed = (input_count+1)*ANCHOR_HEIGHT;
      if(height<needed)
	SetValues(XtNheight,(XtArgVal)needed);
      input_off = height/(input_count+1);
   }

   if(input_count){
      Position off = input_count*input_off;
      Anchor *input = new Anchor(this, io, Anchor::Input, off, Anchor::Left);
      inputs = new iolist(inputs, input);
      input_count--;
   }
}

void ObjectView::AddOutput(IO* io)
{
   if(!output_off)
     output_off = height/(output_count+1);

   if(output_count){
      Position off = output_count*output_off;
      Anchor *output = new Anchor(this, io, Anchor::Output, off, Anchor::Right);
      outputs = new iolist(outputs, output);
      output_count--;
   }
}

void ObjectView::StartMoving()
{
   ((ObjectEditor*)Parent())->StartMovingObject(this);
}

void ObjectView::Move(XPoint& point)
{
   iolist* p;

   SetValues(XtNx,(XtArgVal)point.x, XtNy,(XtArgVal)point.y);
   p = inputs;
   while(p){
      p->anchor->PositionItself();
      p = p->next;
   }
   p = outputs;
   while(p){
      p->anchor->PositionItself();
      p = p->next;
   }

}

#define NOTSET (iolist*)1

Connection* ObjectView::GetInputConnections()
{
   static iolist* p = NOTSET;
   Connection *retVal;

   if(p == NOTSET)
     p = inputs;

   while(p) {
      retVal = p->anchor->Connected();
      p = p->next;
      if(retVal) return retVal;
   }
   p = NOTSET;
   return (Connection*)NULL;
}

Connection* ObjectView::GetOutputConnections()
{
   static iolist* p = NOTSET;
   Connection *retVal;

   if(p == NOTSET)
     p = outputs;

   while(p) {
      retVal = p->anchor->Connected();
      p = p->next;
      if(retVal) return retVal;
   }
   p = NOTSET;
   return (Connection*)NULL;
}

#undef NOTSET

void ObjectView::print_object(ostream& o) { object->print_object(o); }
void ObjectView::print_all(ostream& o) { object->print_all(o); }

void ObjectView::CreateWidgetObject()
{
   object->CreateWidget();
}
