import tango.core.Thread;

import tango.util.locks.Condition;
import tango.util.locks.Mutex;

import tango.text.convert.Integer;

import tango.io.Stdout;

typedef int Color;
const Color BLUE = 0;
const Color RED = 1;
const Color YELLOW = 2;
const Color FADED = 3;

class MeetingPlace
  {
    int remaining_ = 10;
    Mutex lock_;
    Condition condition_;
    Color * swappedColor_ = null;

    this()
      {
        lock_ = new Mutex;
        condition_ = new Condition( lock_ );
      }
  }

class Creature : Thread
  {
    this(
        Color my,
        MeetingPlace meetingPlace )
      {
        super( &run );

        my_ = my;
        meetingPlace_ = meetingPlace;
      }

    void
    run()
      {
        while( true )
          {
            Color other = my_;
            scope localLock = new ScopedLock( meetingPlace_.lock_ );
            if( meetingPlace_.remaining_ )
              {
                if( !meetingPlace_.swappedColor_ )
                  {
                    meetingPlace_.swappedColor_ = &other;
                    meetingPlace_.condition_.wait();
                    meetingPlace_.swappedColor_ = null;
                    --meetingPlace_.remaining_;
                  }
                else
                  {
                    other = *meetingPlace_.swappedColor_;
                    *meetingPlace_.swappedColor_ = my_;
                    meetingPlace_.condition_.notify();
                  }

                localLock.release;
                if( FADED != other )
                  {
                    my_ = complement( other );
                    ++creaturesMeet_;
                  }
              }
            else
              break;
          }
Stdout( "creaturesMeet_: " )( creaturesMeet_ ).newline;
      }

    int
    total()
      {
        return creaturesMeet_;
      }

  private :
    Color my_;
    MeetingPlace meetingPlace_;

    int creaturesMeet_ = 0;

    Color
    complement( Color other )
      {
        switch( my_ )
        {
            case BLUE:
                return other == RED ? YELLOW : RED;
            case RED:
                return other == BLUE ? YELLOW : BLUE;
            case YELLOW:
                return other == BLUE ? RED : BLUE;
            default:
                break;
        }
        return my_;
      }
  }

void
main( char[][] args )
  {
    auto meetingPlace = new MeetingPlace;
    if( 2 == args.length )
      meetingPlace.remaining_ = parse( args[ 1 ], 10 );

    auto colors = [ BLUE, RED, YELLOW, BLUE ];

    Creature[] group;
    foreach( color; colors )
      {
        Creature creature = new Creature( color, meetingPlace );
        group ~= creature;
        creature.start;
      }

    int total = 0;
    foreach( c; group )
      {
        c.join;
        total += c.total;
      }
    Stdout( total ).newline;
  }

// vim:ts=2:sts=2:sw=2:expandtab:fenc=utf-8: