package railo.runtime.type.sql;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import railo.commons.io.IOUtil;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;



/**
 * The representation (mapping) in the Java <sup><small>TM </small> </sup>
 * programming language of an SQL <code>CLOB</code> value. An SQL
 * <code>CLOB</code> is a built-in type that stores a Character Large Object
 * as a column value in a row of a database table. By default drivers implement
 * <code>Clob</code> using an SQL <code>locator(CLOB)</code>, which means
 * that a <code>Clob</code> object contains a logical pointer to the SQL
 * <code>CLOB</code> data rather than the data itself. A <code>Clob</code>
 * object is valid for the duration of the transaction in which is was created.
 * <p>
 * Methods in the interfaces {@link DriverResultSet},{@link CallableStatement},
 * and {@link PreparedStatement}, such as <code>getClob</code> and
 * <code>setClob</code> allow a programmer to access an SQL <code>CLOB</code>
 * value. The <code>Clob</code> interface provides methods for getting the
 * length of an SQL <code>CLOB</code> (Character Large Object) value, for
 * materializing a <code>CLOB</code> value on the client, and for determining
 * the position of a pattern of bytes within a <code>CLOB</code> value. In
 * addition, this interface has methods for updating a <code>CLOB</code>
 * value.
 */
public final class ClobImpl implements java.sql.Clob, Serializable {
    
    
    
  /**
   * cast given value to a clob
 * @param value
 * @return clob
 * @throws PageException 
 */
    public static Clob toClob(Object value) throws PageException {
    	if(value instanceof Clob) return (Clob)value;  
    	else if(value instanceof char[]) 
    		return toClob(new String((char[])value));  
    	else if(value instanceof Reader) {
    		StringWriter sw=new StringWriter();
    		try {
    			IOUtil.copy((Reader)value,sw,false,true);
    		} 
    		catch (IOException e) {
    			throw ExpressionException.newInstance(e);
    		}
    		return toClob(sw.toString());  
    	}
    	return toClob(Caster.toString(value));
    }
    
    public static Clob toClob(String value) {
    	return new ClobImpl(value);
    }
    
    
    
    
  /** The data represented as a string of this <code>CLOB</code> */
  private String stringData = null;

  /**
   * Creates a new <code>Clob</code> instance.
   * 
   * @param data a <code>String</code> of character data
   */
  private ClobImpl(String data) {
    stringData = data;
  }

  /**
   * Returns the size of the <code>CLOB</code> value designated by this
   * <code>Clob</code> object
   * 
   * @return length of the <code>CLOB</code> value that this <code>clob</code>
   *         represents
   * @exception SQLException if there is an error accessing the length of the
   *              <code>CLOB</code>
   */
  public long length() throws SQLException {
	  return stringData.length();
  }

  /**
   * Retrieves the <code>CLOB</code> value designated by this
   * <code>Clob</code> instance as a stream.
   * 
   * @return a stream containing the <code>CLOB</code> data
   * @exception SQLException if there is an error accessing the
   *              <code>CLOB</code> value
   */
  public java.io.InputStream getAsciiStream() throws SQLException	{
	  return new ByteArrayInputStream(stringData.getBytes());
  }

  /**
   * Materializes the <code>CLOB</code> value designated by this <Code>object
   * as a stream of Unicode character.
   * 
   * @return A reader object with all the data in the <code>CLOB</code> value
   *         designated by this clob object as unicode characters.
   * @exception SQLException if there is an error accessing the
   *              <code>CLOB</code> value
   */
  public java.io.Reader getCharacterStream() throws SQLException {
	  return new StringReader(stringData);
  }
  
  public Reader getCharacterStream(long pos, long len) {
	  return new StringReader(stringData.substring((int)pos, (int)len));
  }

  /**
   * Returns a copy of the portion of the <code>CLOB</code> value represented
   * by this <code>CLOB</code> object that starts at position <i>position </i>
   * and has ip to <i>length </i> consecutive characters.
   * 
   * @param pos the position where to get the substring from
   * @param length the length of the substring
   * @return the substring
   * @exception SQLException if there is an error accessing the
   *              <code>CLOB</code>
   */
  public String getSubString(long pos, int length) throws SQLException {
    if (length > stringData.length())
      throw new SQLException("Clob contains only " + stringData.length()
          + " characters (asking for " + length + ").");
    return stringData.substring((int) pos-1, length);
  }

  /**
   * Retrieves the character position at which the specified string
   * <code>searchstr</code> begins within the <code>CLOB</code> value that
   * this <code>Clob</code> object represents. The search for
   * <code>searchstr</code> begins at position <code>start</code>.
   * 
   * @param searchstr the byte array for which to search
   * @param start the position at which to begin searching; the first position
   *          is 1
   * @return the position at which the pattern appears, else -1
   * @exception SQLException if there is an error accessing the
   *              <code>CLOB</code>
   */
  public long position(String searchstr, long start) throws SQLException {
	  return stringData.indexOf(searchstr, (int) start);
  }

  /**
   * Retrieves the character position at which the specified <code>Clob</code>
   * object <code>searchstr</code> begins within the <code>CLOB</code> value
   * that this <code>Clob</code> object represents. The search for
   * <code>searchstr</code> begins at position <code>start</code>.
   * 
   * @param searchstr the byte array for which to search
   * @param start the position at which to begin searching; the first position
   *          is 1
   * @return the position at which the pattern appears, else -1
   * @exception SQLException if there is an error accessing the
   *              <code>CLOB</code>
   */
  public long position(java.sql.Clob searchstr, long start) throws SQLException {
	  return position(searchstr.getSubString(0, (int) searchstr.length()),
        (int) start);
  }

  // -------------------------- JDBC 3.0 -----------------------------------

  /**
   * Retrieves a stream to be used to write Ascii characters to the CLOB value
   * that this Clob object represents, starting at position pos.
   * 
   * @param pos the position where to start the stream
   * @return the ascii outputstream to this <code>clob</code> object
   * @throws SQLException if there is an error accessing the <code>clob</code>
   */
  public OutputStream setAsciiStream(long pos) throws SQLException {
      // TODO impl.
      throw new SQLException("JDBC 3.0 Method setAsciiStream not implemented");
  }

  /**
   * Retrieves a stream to be used to write a stream of Unicode characters to
   * the CLOB value that this Clob object represents, at position pos.
   * 
   * @param pos the position where to start the writer
   * @return the writer to this <code>clob</code> object
   * @throws SQLException if there is an error accessing the <code>clob</code>
   */
  public Writer setCharacterStream(long pos) throws SQLException {
      // TODO impl.
      throw new SQLException("JDBC 3.0 Method setCharacterStream not implemented");
  }

  /**
   * Writes the given Java String to the CLOB value that this Clob object
   * designates at the position pos.
   * 
   * @param pos the position where to set the string
   * @param str string to insert in the <code>clob</code>
   * @return return value
   * @throws SQLException if there is an error accessing the <code>clob</code>
   */
  public int setString(long pos, String str) throws SQLException {
      // TODO impl.
      throw new SQLException("JDBC 3.0 Method setString not implemented");
  }

  /**
   * Writes len characters of str, starting at character offset, to the CLOB
   * value that this Clob represents.
   * 
   * @param pos the position
   * @param str the string
   * @param offset the offset
   * @param len the length
   * @return return value
   * @throws SQLException if there is an error accessing the <code>clob</code>
   */
  public int setString(long pos, String str, int offset, int len) throws SQLException{
      // TODO impl.
      throw new SQLException("JDBC 3.0 Method setString not implemented");
  }

  /**
   * Truncates the CLOB value that this Clob designates to have a length of len
   * characters.
   * 
   * @param len the length
   * @throws SQLException if there is an error accessing the <code>clob</code>
   */
  public void truncate(long len) throws SQLException
  {
      // TODO impl.
      throw new SQLException("JDBC 3.0 Method truncate not implemented");
  }
  

  @Override
  public String toString() {
      return stringData;
  }

	public void free() {
		stringData="";
	}



}