import tango.core.Thread;

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

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;
    Semaphore sem_;
    Mutex lock_;
    Condition meetCompleted_;
    Color * swappedColor_ = null;

    this()
      {
        sem_ = new Semaphore(2);
        lock_ = new Mutex;
        meetCompleted_ = new Condition( lock_ );
      }
  }

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

        my_ = my;
        meetingPlace_ = meetingPlace;
      }

    void
    run()
      {
        while( true )
          {
            Color other = processMeeting;

            if( FADED != other )
              {
                my_ = complement( other );
                ++creaturesMeet_;
              }
            else
              {
                my_ = FADED;
                break;
              }
          }
      }

    int
    total()
      {
        return creaturesMeet_;
      }

  private :
    Color my_;
    MeetingPlace meetingPlace_;

    int creaturesMeet_ = 0;

    Color
    processMeeting()
      {
        Color other = my_;

        with( meetingPlace_ )
          {
            sem_.acquire();

            scope localLock = new ScopedLock( lock_ );

            if( !swappedColor_ )
              {
                scope(exit) sem_.release();

                if( remaining_ )
                  {
                    scope(exit) sem_.release();

                    swappedColor_ = &other;

                    // Wait for second creature to make pair.
                    meetCompleted_.wait();

                    // Clean stuff after the meeting.
                    swappedColor_ = null;
                    --remaining_;
                  }
                else
                  other = FADED;
              }
            else
              {
                // Color exchange.
                other = *swappedColor_;
                *swappedColor_ = my_;

                // The first creature may wake up.
                meetCompleted_.notify();
              }
          }

        return other;
      }

    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: