// 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 "stream.h"
#include <X11/StringDefs.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, Type type, Position offset, Location loc)
     : host(host), type(type), connected(NULL),  GripWidget(type==Input ? "input" : "output", host->Parent())
{
   SetValues(XtNborderWidth, (XtArgVal) 1);
   AddCallback(XtNcallback, ObjectConnection, (XtPointer)NULL);

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

   PositionItself(offset,loc);
}

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);	// 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[] = " <Btn3Down>:		ObjectViewManager(Move)";

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;
    default:
      cerr << "ObjectViewManager: unknown action\n";
   }
}

Boolean ObjectView::class_inited = False;

ObjectView::ObjectView(Widget workspace, String name)
: LabelWidget(name,workspace), inputs(NULL), outputs(NULL)
{
   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;
   }
}

void ObjectView::AddInput(Cardinal count)
{
   Position off;
   Cardinal i = count;
      
   // XXX
   off = height/(count+1);

   while(i){
      Anchor *input = new Anchor(this, Anchor::Input, i*off, Anchor::Left);
      inputs = new iolist(inputs, input);
      i--;
   }
}

void ObjectView::AddOutput(Cardinal count)
{
   Position off;
   Cardinal i = count;
      
   // XXX
   off = height/(count+1);

   while(i){
      Anchor *output = new Anchor(this, Anchor::Output, i*off, Anchor::Right);
      outputs = new iolist(outputs, output);
      i--;
   }
}

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
