#include "CLHEP/Vector/defs.h"

#include <cctype>
#include <iostream>

namespace {

bool eatwhitespace ( std::istream & is ) {
  // Will discard whitespace until it either encounters EOF or bad input
  // (in which case it will return false) or it hits a non-whitespace.  
  // Will put that non whitespace character back so that after this routine
  // returns true, is.get(c) should always work.
  // If eatwhitespace returns false, is will always be in a fail or bad state.
  char c;
  bool avail = false;	// avail stays false until we know there is a nonwhite
			// character available.
  while ( is.get(c) ) {
    if ( !isspace(c) ) {
      is.putback(c);
      avail = true;
      break;
    }
  }
  return avail;
}

void fouledup() {
  std::cerr << "istream mysteriously lost a putback character!\n";
}


} // end of unnamed namespace


namespace CLHEP  {

void ZMinput3doubles ( std::istream & is, const char * type,
			double & x, double & y, double & z ) {

// Accepted formats are 
// x y z
// x, y, z (each comma is optional, and whitespace ignored if comma present)
// ( x, y, z ) (commas optional)

  char c;
  bool parenthesis = false;

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before trying to input " << type << "\n";
    return;
  }

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == '(' ) {
    parenthesis = true;
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended after ( trying to input " << type << "\n";
      return;
    }
  } else {
    is.putback(c);
  }  

  // At this point, parenthesis or not, the next item read is supposed to
  // be the number x.

  if (!(is >> x)) {
    std::cerr << "Could not read first value in input of " << type << "\n";
    return;
  }

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before second value of " << type << "\n";
    return;
  } 

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == ',' ) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended ater one value and comma in " 
							<< type << "\n";
      return;
    }
  } else {
    is.putback(c);
  }

  // At this point, comma or not, the next item read is supposed to
  // be the number y.

  if (!(is >> y)) {
    std::cerr << "Could not read second value in input of " << type << "\n";
    return;
  }

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before third value of " << type << "\n";
    return;
  } 

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == ',' ) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended ater two values and comma in " 
							<< type << "\n";
      return;
    }
  } else {
    is.putback(c);
  }

  // At this point, comma or not, the next item read is supposed to
  // be the number z.

  if (!(is >> z)) {
    std::cerr << "Could not read third value in input of " << type << "\n";
    return;
  }

  // Finally, check for the closing parenthesis if there was an open paren.

  if (parenthesis) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "No closing parenthesis in input of " << type << "\n";
      return;
    } 
    if ( !is.get(c) ) { fouledup(); return; }
    if ( c != ')' ) {
      std::cerr << "Missing closing parenthesis in input of " 
							<< type << "\n";
      // Now a trick to do (as nearly as we can) what 
      // is.putback(c); is.setstate(std::ios_base::failbit); 
      // would do (because using ios_base will confuse old CLHEP compilers):
      if ( isdigit(c) || (c=='-') || (c=='+') ) {
        is.putback('@');
      } else {
        is.putback('c');
      }
      int m;
      is >> m;  // This fails, leaving the state bad, and the istream
		// otherwise unchanged, except if the next char might
		// have started a valid int, it turns to @
      return;
    }
  }

  return;

}


void ZMinputAxisAngle ( std::istream & is, 
			double & x, double & y, double & z, 
			double & delta ) {
// Accepted formats are 
// parenthesis optional, then
// any acceptable format for a Hep3Vector, then
// optional comma, then
// delta, then
// close parenthesis if opened at start.
//
// But if there is an open parenthesis, it must be for the overall
// object.  That is, if the axis has parentheses, the form must be 
// ( (x,y,z) , delta ) 

  char c;
  bool parenthesis = false;

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before trying to input AxisAngle \n";
    return;
  }

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == '(' ) {
    parenthesis = true;
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended after ( trying to input AxisAngle \n";
      return;
    }
  } else {
    is.putback(c);
  }  

  // At this point, parenthesis or not, the next item read is supposed to
  // be a valid Hep3Vector axis.

  ZMinput3doubles ( is, "axis of AxisAngle", x, y, z );
  if (!is) return;

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before delta of AxisAngle \n";
    return;
  } 

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == ',' ) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended ater axis and comma in AxisAngle \n"; 
      return;
    }
  } else {
    is.putback(c);
  }

  // At this point, comma or not, the next item read is supposed to
  // be the number delta.

  if (!(is >> delta)) {
    std::cerr << "Could not delta value in input of AxisAngle \n";
    return;
  }

  // Finally, check for the closing parenthesis if there was an open paren.

  if (parenthesis) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "No closing parenthesis in input of AxisAngle \n";
      return;
    } 
    if ( !is.get(c) ) { fouledup(); return; }
    if ( c != ')' ) {
      std::cerr << "Missing closing parenthesis in input of AxisAngle \n";
      if ( isdigit(c) || (c=='-') || (c=='+') ) {
        is.putback('@');
      } else {
        is.putback('c');
      }
      int m;
      is >> m;  // This fails, leaving the state bad.
      return;
    }
  }

  return;

}


void ZMinput2doubles ( std::istream & is, const char * type,
			double & x, double & y ) {

// Accepted formats are 
// x y 
// x, y (comma is optional, and whitespace ignored if comma present)
// ( x, y ) (comma optional)

  char c;
  bool parenthesis = false;

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before trying to input " << type << "\n";
    return;
  }

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == '(' ) {
    parenthesis = true;
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended after ( trying to input " << type << "\n";
      return;
    }
  } else {
    is.putback(c);
  }  

  // At this point, parenthesis or not, the next item read is supposed to
  // be the number x.

  if (!(is >> x)) {
    std::cerr << "Could not read first value in input of " << type << "\n";
    return;
  }

  if ( !eatwhitespace(is) ) {
    std::cerr << "istream ended before second value of " << type << "\n";
    return;
  } 

  if ( !is.get(c) ) { fouledup(); return; }
  if ( c == ',' ) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "istream ended ater one value and comma in " 
							<< type << "\n";
      return;
    }
  } else {
    is.putback(c);
  }

  // At this point, comma or not, the next item read is supposed to
  // be the number y.

  if (!(is >> y)) {
    std::cerr << "Could not read second value in input of " << type << "\n";
    return;
  }

  // Finally, check for the closing parenthesis if there was an open paren.

  if (parenthesis) {
    if ( !eatwhitespace(is) ) {
      std::cerr << "No closing parenthesis in input of " << type << "\n";
      return;
    } 
    if ( !is.get(c) ) { fouledup(); return; }
    if ( c != ')' ) {
      std::cerr << "Missing closing parenthesis in input of " 
							<< type << "\n";
      // Now a trick to do (as nearly as we can) what 
      // is.putback(c); is.setstate(std::ios_base::failbit); 
      // would do (because using ios_base will confuse old CLHEP compilers):
      if ( isdigit(c) || (c=='-') || (c=='+') ) {
        is.putback('@');
      } else {
        is.putback('c');
      }
      int m;
      is >> m;  // This fails, leaving the state bad, and the istream
		// otherwise unchanged, except if the next char might
		// have started a valid int, it turns to @
      return;
    }
  }

  return;

}

}  // namespace CLHEP
