/// <summary>
 /// Initialize a new document link
 /// </summary>
 public void CreateDocumentLink()
 {
     _range = new CellRangeAddress(0, 0, 0, 0);
     _guid = STD_MONIKER;
     _linkOpts = HLINK_LABEL | HLINK_PLACE;
     Label = "";
     _moniker = FILE_MONIKER;
     Address = "";
     TextMark = "";
 }
 /// <summary>
 /// Initialize a new url link
 /// </summary>        
 public void CreateUrlLink()
 {
     _range = new CellRangeAddress(0, 0, 0, 0); 
     _guid = STD_MONIKER;
     
     _linkOpts = HLINK_URL | HLINK_ABS | HLINK_LABEL;
     Label = "";
     _moniker = URL_MONIKER;
     Address = "";
     _uninterpretedTail = URL_uninterpretedTail;
 }
 /// <summary>
 /// Initialize a new file link
 /// </summary>
 public void CreateFileLink()
 {
     _range = new CellRangeAddress(0, 0, 0, 0);
     _guid = STD_MONIKER;
     _linkOpts = HLINK_URL | HLINK_LABEL;
     _fileOpts = 0;
     Label = "";
     _moniker = FILE_MONIKER;
     Address= null;
     ShortFilename = "";
     _uninterpretedTail = FILE_uninterpretedTail;
 }
        /**
         * Read hyperlink from input stream
         *
         * @param in the stream to Read from
         */
        public HyperlinkRecord(RecordInputStream in1)
        {
            _range = new CellRangeAddress(in1);

            // 16-byte GUID
            _guid = new GUID(in1);

            /*
             * streamVersion (4 bytes): An unsigned integer that specifies the version number
             * of the serialization implementation used to save this structure. This value MUST equal 2.
             */
            int streamVersion = in1.ReadInt();
            if (streamVersion != 0x00000002)
            {
                throw new RecordFormatException("Stream Version must be 0x2 but found " + streamVersion);
            }
            _linkOpts = in1.ReadInt();

            if ((_linkOpts & HLINK_LABEL) != 0)
            {
                int label_len = in1.ReadInt();
                _label = in1.ReadUnicodeLEString(label_len);
            }
            if ((_linkOpts & HLINK_TARGET_FRAME) != 0)
            {
                int len = in1.ReadInt();
                _targetFrame = in1.ReadUnicodeLEString(len);
            }
            if ((_linkOpts & HLINK_URL) != 0 && (_linkOpts & HLINK_UNC_PATH) != 0)
            {
                _moniker = null;
                int nChars = in1.ReadInt();
                _address = in1.ReadUnicodeLEString(nChars);
            }
            if ((_linkOpts & HLINK_URL) != 0 && (_linkOpts & HLINK_UNC_PATH) == 0)
            {
                _moniker = new GUID(in1);

                if (URL_MONIKER.Equals(_moniker))
                {
                    int length = in1.ReadInt();
                    /*
                     * The value of <code>length<code> be either the byte size of the url field
                     * (including the terminating NULL character) or the byte size of the url field plus 24.
                     * If the value of this field is set to the byte size of the url field,
                     * then the tail bytes fields are not present.
                     */
                    int remaining = in1.Remaining;
                    if (length == remaining)
                    {
                        int nChars = length / 2;
                        _address = in1.ReadUnicodeLEString(nChars);
                    }
                    else
                    {
                        int nChars = (length - TAIL_SIZE) / 2;
                        _address = in1.ReadUnicodeLEString(nChars);
                        /*
                         * TODO: make sense of the remaining bytes
                         * According to the spec they consist of:
                         * 1. 16-byte  GUID: This field MUST equal
                         *    {0xF4815879, 0x1D3B, 0x487F, 0xAF, 0x2C, 0x82, 0x5D, 0xC4, 0x85, 0x27, 0x63}
                         * 2. Serial version, this field MUST equal 0 if present.
                         * 3. URI Flags
                         */
                        _uninterpretedTail = ReadTail(URL_uninterpretedTail, in1);
                    }
                }
                else if (FILE_MONIKER.Equals(_moniker))
                {
                    _fileOpts = in1.ReadShort();

                    int len = in1.ReadInt();
                    _shortFilename = StringUtil.ReadCompressedUnicode(in1, len);
                    _uninterpretedTail = ReadTail(FILE_uninterpretedTail, in1);
                    int size = in1.ReadInt();
                    if (size > 0)
                    {
                        int charDataSize = in1.ReadInt();

                        //From the spec: An optional unsigned integer that MUST be 3 if present
                        int optFlags = in1.ReadUShort();
                        if (optFlags != 0x0003)
                        {
                            throw new RecordFormatException("Expected 0x3 but found " + optFlags);
                        }
                        _address = StringUtil.ReadUnicodeLE(in1, charDataSize / 2);
                    }
                    else
                    {
                        _address = null;
                    }
                }
                else if (STD_MONIKER.Equals(_moniker))
                {
                    _fileOpts = in1.ReadShort();

                    int len = in1.ReadInt();

                    byte[] path_bytes = new byte[len];
                    in1.ReadFully(path_bytes);

                    _address = Encoding.UTF8.GetString(path_bytes);
                }
            }

            if ((_linkOpts & HLINK_PLACE) != 0)
            {
                int len = in1.ReadInt();
                _textMark = in1.ReadUnicodeLEString(len);
            }

            if (in1.Remaining > 0)
            {
                Console.WriteLine(HexDump.ToHex(in1.ReadRemainder()));
            }
        }